home *** CD-ROM | disk | FTP | other *** search
/ BMUG PD-ROM A / PD-ROM A.iso / Programming / Programming Languages / MacOberon / MacOberon (docs) / WriteReport.Txt (.txt) < prev   
Encoding:
Oberon Text  |  1991-02-06  |  153.0 KB  |  1,944 lines  |  [.Ob./.Ob2]

  1. Syntax10.Scn.Fnt
  2. LetterHead.Scn.Fnt
  3. Syntax14.Scn.Fnt
  4. Syntax20.Scn.Fnt
  5. Syntax20m.Scn.Fnt
  6. Syntax16m.Scn.Fnt
  7. Syntax12.Scn.Fnt
  8. Syntax14m.Scn.Fnt
  9.     Syntax12i.Scn.Fnt
  10. Syntax8.Scn.Fnt
  11. Syntax10i.Scn.Fnt
  12. Syntax12b.Scn.Fnt
  13. Syntax12m.Scn.Fnt
  14. Syntax10b.Scn.Fnt
  15. Eidgen
  16. ssische    Departement Informatik
  17. Technische Hochschule    Institut f
  18. rich    Computersysteme
  19. Clemens A. Szyperski    Write
  20.     An Extensible Text Editor
  21.     for the Oberon System
  22. January 1991
  23. Author's address:
  24. Computersysteme
  25. ETH-Zentrum
  26. CH-8092 Zurich, Switzerland
  27. e-mail: szyperski@inf.ethz.ch
  28. Write - An Extensible Text Editor for the Oberon System
  29. Clemens A. Szyperski
  30. Abstract
  31. Extensible software systems open the opportunity of reducing complexity by trading off initial functionality. Instead of building everything into a monolythic system, a core system with a certain built-in potential for later extension is separated from an arbitrarily rich set of extensions (and, in principle, extensions of extensions). This report concentrates on the text editor Write and some of its existing extensions. It is discussed how extensibility is opened but also limited by the core system's design. It is observed that a potentially very rich and flexible basic framework may actually lead to few or no implemented extensions if the chosen model was inadequately complex for the typical extension demands. On the other hand, a too rigid core structure may impose limits preventing useful extensions right from the beginning.
  32. Keywords: Extensibility, Editor, Oberon
  33. Introduction
  34. Making a text editor extensible is a rich source of conceptual variations [e.g. WeGaMa89] and already in itself an interesting project target. However, other aspects need to be considered that are not directly related to the aim of engineering an extensible editor. First of all, the fact that extensions are possible in a given framework needs to be paralleled by sufficient ease of actually implementing extensions. Secondly, for the editor to be useful it is important to integrate it well into its environment. Finally, the interaction of environmental support, potential for extensibility and ease of use need to add up to the user's impression of working with a rather light-weight system. The impression of working with a heavy-weight system should be avoided whenever possible, as the resulting barrier might well doom the newly created tool not to be used.
  35.     On the first sight, there is a contradiction between having a rich, extensible, and flexible system, and having a light-weight, easy to master, and easy to understand tool. The key to an adequate compromise lies in the approach to extensibility. An unextensible system with a rich set of well designed, built-in functionality might well be easier to master than an extensible but highly complicated system. For an extensible system to be transparent and manageable, there are two criteria of dominant importance. On one hand, the core system without any (visible) extensions must follow a clean and easy to understand model. This model is the basis for understanding the whole system and the framework to learn more about available extensions. On the other hand, extensions should be restricted to a few atomic features of the core system. Otherwise, the user can hardly predict the effects of incorporating a certain extension into the editing tool. The importance of these two points might be stressed by considering that extensions naturally evolve at different places and that vendors of extensions might hardly know about each other or about the potentially harmful interaction of their extensions when all put together.
  36.     The Write editor limits extensibility to only two orthogonal domains: commands and characters. The concept of extending the system by adding new commands is already well-established in the Oberon system [WiGu88]. Adding a new command adds new manipulational capabilities applicable by the user. The second domain of extensions is the actual data that commands operate on. To keep the interdependence of commands and data extensions low, data extensions should be organized around the atomic components of the data structures provided by the core framework. In the case of a text editor, the core data structure is text, i.e. a sequence of characters. As a result, the natural anchor of data extensibility is considered to be the character. In fact, no other form of data extension is supported by Write; especially, there are no provisions for overlaying a text with additional structures. While this might sound too rigorous, it results in a relatively compact and easy to understand design. Also, the remaining potential for extensibility is easy to explore and still quite rich.
  37.     As Write is integrated into the Oberon system, the natural environment are the existing standard text class [Gu90], and all commands operating on such texts. The latter range from simple formatting commands (e.g. changing the font of a selected text stretch) to complex transformations (including the Oberon compiler). While in most aspects, Write integrates well with the Oberon system, there are certain shortcomings. These will be discussed in more detail later. For the time being, it should suffice it to say that the main problem is the traditional procedural interface to the Oberon system's standard data types, including texts, which limits the system's potential for extensibility.
  38.     The remainder of this report is organized into two main parts and a set of appendices. The first part concentrates on the core system of Write, its major design decisions and related tradeoffs, and its potential for extensibility. The second part presents the details on how Write extensions work, as well as a series of successively more advanced extensions together with practical and available examples. The appendices cover a user manual for the Write core system, a description of the programmer's interfaces of the Write core modules, and two complete sources of actual extensions. Care has been taken to decouple the various parts of this report. It should be possible to read each of them independently. A few exceptions to this rule are clearly marked using cross-references.
  39. Part 1 - The Write Core System
  40. This part deals with the standard modules of the Write system. The following sections form a stepwise analysis of the provided functionality, the design criteria, and the resulting tradeoffs. The involved modules and levels of abstraction are discussed bottom-up, while the concepts of extensibility are introduced top-down.
  41. 1.1  Approaching Extensibility - Coping with Complexity
  42. The importance of limiting extensibility in a system to a few strategically well chosen points has been stressed in the introduction. As mentioned, for a text seen as a sequence of characters, the natural atoms of extensibility are just the characters themselves. Other possibilities are higher-level structures layered over a basic text such as paragraphs, links between text areas, and the like. The Write system restricts itself to the extension of characters. Such extended characters are called elements. In order to understand what it actually means "to extend a character", the properties of unextended characters as members of texts are examined in the following.
  43. Standard Oberon Texts
  44. A character is an atomic instance: it can neither be split nor merged with other characters. Associated with a character are certain attributes that are fully independent of the neighbouring characters. Texts of such attributed characters are implemented in the Oberon standard module Texts.
  45. Table 1. Oberon Character Attributes.
  46. Furthermore, a character is the natural atomic editing unit of a text. Characters (or sequences thereof) may be inserted, copied, or deleted, and their attributes may be changed. This is achieved by allowing the user to select any subsequence of characters within a text, and by supporting the placement of a caret between any two characters. The user may delete selected text, copy it over to the position marked by the caret, change its attributes, or enter new characters at the caret position. Finally, for the sake of simplicity, characters are also displayed as atomic units, i.e. either the whole character can be displayed within the available screen area, or it is suppressed (clipped) completely.
  47.     In order to maintain the very simple and efficient text editing model of the Oberon system, it is desirable to design extension features such that they will not get into the way of the editing model. The only way to achieve this is the restriction to extensions that do not leave the principal conceptual properties of texts seen as a sequence of characters. It will be shown later, that within this carefully chosen restriction, the remaining potential for extensions is still quite large.
  48.     Another important aspect of Oberon texts is that programs have access to any text displayed on the screen. For example, it is possible to directly compile a displayed text without storing it beforehand. This open accessibility of data structures attached to viewers is an important key to the Oberon system's integration among various applications (better: command packages, as Oberon has no primary notion of distinct applications). Therefore, it is important that a functionally enriched text editing environment interacts and cooperates as seamlessly as possible with the existing text and text editing facilities.
  49. Write Texts
  50. The above arguments lead to the design of a first core module of Write: WriteTexts. To reach the desired integration, WriteTexts should be an extension of the Oberon standard module Texts. To reach the extensibility aimed at, WriteTexts introduces the notion of extended characters, so called elements. Texts defines two basic types of textual representation: texts and buffers. Buffers are used to accumulate output of commands in order to insert them as a whole into a target text (delayed and atomic output). Furthermore, Texts defines readers and writers as access structures to texts and buffers, respectively. WriteTexts implements extended buffers and texts representing sequences of potentially extended characters, while it does not extend the access structures of Texts. It does, however, add some access primitives to operate on extended characters. Hence, existing clients of Texts can use readers and writers (see restrictions below) to access WriteTexts texts. The following table is an excerpt of the WriteTexts interface.
  51.     Buffer = POINTER TO BufferDesc;
  52.     BufferDesc = RECORD(Texts.BufDesc) END;
  53.     Text = POINTER TO TextDesc;
  54.     TextDesc = RECORD(Texts.TextDesc) END;
  55. (* Buffers *)
  56. PROCEDURE OpenBuf(B: Buffer);
  57. PROCEDURE CopyBuf(SB: Buffer; VAR DB: Buffer);
  58. (* Texts *)
  59. PROCEDURE Open(T: Text; name: ARRAY OF CHAR);
  60. PROCEDURE Delete(T: Text; beg, end: LONGINT);
  61. PROCEDURE SaveBuf(T: Texts.Text; beg, end: LONGINT; VAR B: Buffer);
  62. PROCEDURE Insert(T: Text; pos: LONGINT; B: Texts.Buffer);
  63. There are no primitives available in WriteTexts to read or write plain characters. The standard procedures defined in module Texts can be used with the exception that a writer's buffer must not be inserted into a Write text using the Texts.Insert procedures but must be inserted using the corresponding procedures of WriteTexts. The same holds for the other shown text and buffer manipulation procedures. It is a shortcoming of the procedural interface of Texts that extensions cannot override functionality. A future version of Write might well be based on a different version of Texts that either has a class-centered interface (i.e. methods instead of procedures), or has a built-in notion of "extensible characters".
  64.     It is now possible to augment the WriteTexts interface by certain procedures to insert and retrieve elements from a text. As elements should not get into the way of clients of plain texts, each element is represented by a special character (ASCII Code 1CH). Whenever a Reader returns this special character, an application knowing about elements might retrieve the associated element. This way all standard character attributes are attached to the representing character and elements thus inherit them. Therefore procedures of Texts that merely change attributes of character ranges within a text need no changes at all and thus have no counterparts in WriteTexts. The following definition gives the base type of elements, as well as a set of procedures that may be used to operate on elements. After changing an element, the programmer has to call ChangedElem, which propagates a notification to potentially update the display. Note that the interface hides the internal structure used to hold elements of a text. (In the current implementation, a linear list is used.)
  65. CONST
  66.     ElemChar = 1CX;
  67.     Elem = POINTER TO ElemDesc;
  68.     Handler = PROCEDURE(E: Elem; VAR msg: Display.Message);
  69.     ElemDesc = RECORD
  70.         DX, W, H: LONGINT;    (*DX >= W*)
  71.         handle: Handler
  72.     END;
  73.     CopyMsg = RECORD
  74.         e: Elem
  75.     END;
  76. PROCEDURE OpenElem(E: Elem; handle: Handler; dx, w, h: LONGINT);
  77. PROCEDURE InsertElem(T: Text; pos: LONGINT; E: Elem);
  78. PROCEDURE ElemAt(T: Text; pos: LONGINT): Elem;    (*returns NIL if not found*)
  79. PROCEDURE ChangedElem(E: Elem);
  80. Elements are active objects: An installed handler allows object-centered interpretation of messages sent to an object. The messages understood by an element will be defined in the course of this report when needed. To enable delegation of existing display-system messages, the used basetype is the standard Oberon type Display.Message. Like characters, elements are characterized by a bounding box, plus some offset to the next character. (Cf. Fig. 1. Actually, the geometric model used for characters is slightly more complicated. This will be discussed in the section on WriteFrames.) Both, handler and geometry are set when opening an element. The element may then be inserted. Elements floating in a text can be retrieved by means of their current position in the text. Typically, a client asks for an element at a certain position after a Reader returned the special character WriteTexts.ElemChar.
  81. Figure 1. Geometry of an Element.
  82. Generic copying of elements is implemented by sending a copy message. The recipient allocates and initialized a copy of itself. To support delegating a partially processed copy message, a new element should only be allocated and assigned to the message field e, if e is NIL. (Note that the message record is a reference parameter to the receiving handler.)
  83. 1.2  Load & Store - The Write File Format
  84. Loading and storing texts that contain arbitrary extensions is a bit more involved than dealing with fixed file formats. Three points stood in the foreground when deciding on the file format for Write. First of all, element designers should be able to decide on their own, how to store an elements data. Secondly, Write should be able to load existing plain text files, and, conversely, existing clients of plain text files should be able to open a Write text in a meaningful way. Thirdly, Write should be able to gracefully degrade upon detecting unloadable elements, henceforth called aliens, within a Write file.
  85.     The generic loading and storing of elements is implemented by sending special messages to elements. Since an element cannot receive a load message unless it already exists, loading has to be separated from allocation. When storing itself, an element first writes out the name of an Oberon command. This command is called at loadtime to allocate an instance of the element to load, followed by a load message sent to the new element. Loading can fail if the implementing module is not available on the local machine. If loading fails, the resulting alien element is reduced to an anonymous frame-shaped element remembering its data block in the original file. (To make this possible, the file contains the length of each element block.) Such an alien has the original element's frozen bounding box and can be copied or deleted freely. It will return to its normal behaviour as soon as the containing text is loaded with the implementing module available. (In the following and in the remainder of the report, the convention is used that definitions are qualified using the defining module. As above, qualification is left out, if the definition belongs to the module treated in the corresponding section.)
  86.     WriteTexts.LoadMsg = RECORD(Display.FrameMsg)
  87.         r: Files.Rider
  88.     END;
  89.     WriteTexts.StoreMsg = RECORD(Display.FrameMsg)
  90.         r: Files.Rider
  91.     END;
  92. The compatibility to the existing Texts file format is reached by true extension, i.e a Write file block consists of a Texts file block followed by a Write extension block. Customers of Texts file blocks will see a projection of a Write file that reduces all contained elements to a single character. Storing such a text with a non-Write editor will of course remove all Write specific data including all contained elements. The following syntax defines the Write file block format. All values are written as portable compact numbers as defined in [Te90]. The values pos, DX, W, and H are handled outside of the generic load/store mechanism and the byte count bytes always takes four bytes. Both together is used to correctly handle alien elements. The tag values are exported by WriteTexts.
  93. TextBlock = Texts.TextBlock textTag ElemBlock { ElemBlock }.
  94. ElemBlock = elemTag pos DX W H bytes { byte }bytes.
  95.     (*The load command belongs to the element specific data, i.e. is contained in the { byte } sequence*)
  96. 1.3  Visual Interpretation of Write Texts - WriteFrames
  97. The visual interpretation of a data structure covers displaying (part of) the structure within a frame and accepting display dependent edit operations. As WriteFrames behaves very much like the standard Oberon TextFrames, this section concentrates on the casting of individual displayed text lines. For any given line, a left margin, a maximal line width, a minimal line height lsp, a base line offset (descender) dsr, and a grid option are defined. How these values are associated with text lines is explained in the next section. If the grid option is not set, the resulting line height is at least lsp, but extended to the above and below to fully cover all characters (and elements) falling into that line (cf. Fig. 2). If the grid option is set (fig. 3), the line height and base line offset are incremented in steps of the minimal line height lsp, such that lines extended due to larger contained characters (or elements) fall onto the same grid as non-extended lines. From a typographical point of view, the resulting line spacing has nicer properties, while at the same time it may cause irritation by introducing unexpected white space into a text.
  98. Figure 2. Line casting without grid option.
  99. Figure 3. Line casting with grid option.
  100. An element to be displayed within a line receives a prepare message. In turn, the element might change its geometry to adopt to its current environment (for examples, cf. 2.4). Then, the element is casted into a line and clipped against the frame boundaries. If the element is fully visible, it receives a draw message and in turn displays itself. Prepare and draw messages contain the font and color set for the element. The vertical offset attribute for elements is directly interpreted by WriteFrames.
  101.     WriteFrames.PrepareMsg = RECORD(Display.FrameMsg)
  102.         fnt: Fonts.Font;
  103.         col: SHORTINT;
  104.         unit: LONGINT;    (*units per device pixel*)
  105.         indent: LONGINT;    (*width already consumed in line, in units*)
  106.         printing: BOOLEAN;
  107.         pno: INTEGER;    (*page number, valid if printing*)
  108.     END;
  109.     WriteTexts.DrawMsg = RECORD(Display.FrameMsg)
  110.         fnt: Fonts.Font;
  111.         col: SHORTINT;
  112.         unit: LONGINT;    (*units per device pixel*)
  113.         frame: Display.Frame;    (*containing frame in device space*)
  114.         X0, Y0: INTEGER    (*absolute left-bottom coordinate in device space*)
  115.     END;    
  116. The messages have a special unit field that reflects the fact that WriteTexts has no knowledge of particular output devices and their resolution. Hence, all measures maintained within WriteTexts are in device independent units. For a precise definition of device independent units refer to appendix B. A typical conversion of, say, the width of an element elem to device space upon receiving message msg is a simple division: elem.W DIV msg.unit.
  117.     WriteFrames contains a sophisticated screen update mechanism to avoid unnecessary drawing to the screen. Typical edit operations, like character insertion and deletion, have local effects only. If necessary, preservable but ill-positioned portions of the frame contents are moved using fast bitblock transfers. The screen updating is the most complex operation contained in the Write system and thus has been optimized for frequent operations, only. Less frequent operations like viewer repositioning have not been fully optimized. However, in nearly all cases the implementation tries to avoid unnecessary screen flicker, for some rarely used operations this is done at the cost of increased processing time.
  118.     The screen update mechanism is based on a descriptor list attached to each frame. The list contains bounding box and positional information for each line displayed in the frame. When a change notification is received from the displayed text, the frame update proceeds in two phases. First of all, it is checked whether the change can at all affect the frame. If so, it is checked whether the frame origin (i.e. the position of the first displayed character) is affected. If it is affected, it is forced to a line start, and the whole frame is recasted. Hence, editing in the first displayed line can become rather inefficient. If the origin is not affected, two synchronization points in the descriptor list are seeked: The last descriptor which is still valid (as seen from top to bottom), and the first descriptor which is again valid if the corrsponding line is displaced by a certain offset. Between these two points and perhaps behind the last old descriptor, new descriptors are computed, i.e. the affected lines are casted. Once the descriptor list is reestablished, it is determined what amount of screen update needs to be performed and what screen blocks merely need to be shifted using bitblock moves. (Currently, it is not tried to preserve an unchanged suffix of an affected line.)
  119. 1.4  From Elements to Parcs - Introduction of Paragraph Structure
  120. The previous section introduced several new attributes (e.g. line heights) that are no longer logically attached to a single character but to a range of characters (falling into a certain range of lines). It is tempting to introduce a new structure, overlayed over the existing text model, to implement such "paragraph" attributes. This temptation is resisted to stay with the initial principle not to introduce any extensions to texts other than extended characters. Hence, if no added explicit structures are to be used, it is necessary to introduce implicit structures.
  121.     A first approach to map paragraph attributes into a character stream would be the introduction of opening and closing bracket-like characters. However, this has the main disadvantage of unclear semantics in the case of overlapping paragraph ranges. Another problem is the switch of paragraph attributes in the middle of a line. The way chosen in Write is to indeed introduce special paragraph control characters, called parcs for short. The range of a parc is defined implicitly as reaching up to the next following parc or to the end of the text if there is no successor. To avoid the dilemma of inserting a parc into the middle of a line, a parc is always as wide as a whole line and hence is forced by the line casting algorithm onto the next line when not encountered at the beginning of a line.
  122. Figure 4. Effect of inserting a parc into a text.
  123. The definition of a parc and operations thereon are listed below. A special procedure WriteTexts.ParcBefore allows for retrieving the parc valid for a certain text position. If the position is before the first parc within a text, a default parc attached to every Write text is returned.
  124.     Parc = POINTER TO ParcDesc;
  125.     ParcDesc = RECORD(ElemDesc)
  126.         left, width, lead, lsp, dsr: LONGINT;    (*in units*)
  127.         opts: SET;
  128.         nofTabs: INTEGER;
  129.         tab: ARRAY MaxTabs OF LONGINT    (*in units*)
  130.     END;
  131. PROCEDURE ParcBefore(T: Text; pos: LONGINT): Parc;
  132.     (*returns T.defParc if none found*)
  133. PROCEDURE ParcExtent(P: Parc): LONGINT;
  134.     (*parc P extends over range [ElemPos(P), ParcExtent(P) )*)
  135. The many attributes associated with a parc can be queried and set using state messages. The commands Write.Get and Write.Set send such messages to the selected parc. However, for some attributes, direct manipulation would be preferable. Examples are the settings of tabulators, the left margin, the maximal line width, and the like. This is implemented in module WriteParcs as an extension of the parcs defined in WriteTexts. The precise meaning of all parc attributes and whether they can be set interactively is defined in Appendix A. While implementing a separated extension (the interactive changing of parc attributes), WriteParcs also defines a global variable used as prototype for default parcs when opening a new text. Therefore, WriteParcs is known by the text opening command Write.Open and thus belongs to the Write core system.
  136.     StateMsg = RECORD(Display.FrameMsg)
  137.         id: INTEGER;
  138.         par: Texts.Scanner;    (*scanned by receiver to extract individual parameters*)
  139.         log: Texts.Text;    (*used by receiver to log error messages*)
  140.         unit: LONGINT;
  141.         frame: WriteFrames.Frame
  142.     END;    
  143. 1.5  Printing - Options and Compromises
  144. When printing a text edited with an interactive, screen oriented editor, one often asks for "what you see is what you get" (wysiwyg) functionality, i.e. the image visible on the screen is more or less the same as what will be printed. Of course, this is limited by the screen resolution which is typically far lower than that of a modern laser printer. However, far worse is the effect of different resolution when considering rastered fonts. Since the current Write release does not reside on some device independent font model (e.g. PostScript or Display-PostScript [Ad85]), and the used pre-rastered fonts have been tuned by hand for optimal results on screen and on printer, each single character introduces an error in the order of 10%. As this is a systematic effect of the font tuning, the error does not average out, and assuming that optimal spacing is used on screen and on printer, a line of text is about 10 to 15% longer when printed than it is on screen. Note that a similar effect occurs if the fonts used on the screen are substituted by some other fonts when printing, as is done in some Oberon implementations e.g. for the Macintosh [Fr90] and Sparc [Te90] machines.
  145.     There are several possible solutions to this problem, and the solution chosen for Write is fully encapsulated in module Write itself (and could be changed easily). One could use the screen coordinates as dominating grid and force each printed character slightly besides its optimal position. This leads to optimal screen but sub-optimal printer output, as a misadjustment of >10% is visible for certain character constallations. The other way round, the printer coordinates could be chosen to dominate, leading to a sub-optimal screen output. This seems unacceptable, as the screen is the medium the author permanently looks at. Finally, a mixed strategy can be chosen. For example, one might decide to tolerate certain adjustment errors on the screen if text is formatted in a left flush fashion, etc. In Write a different approach has been chosen: When printing a text it is recasted for optimal printer results and thereby allowing for both, optimal screen and printer outputs. However, the price paid is a certain loss of wysiwyg functionality, as line breaks on the screen do no longer correspond to line breaks on the printer.
  146.     To be able to recast printed text according to printer font metrics requires some minimal information about printer metrics to be available to the print algorithm. Hence, Write uses a special metrics file per font family containing the horizontal displacement information for each character in each style available in the corresponding font family. These files are taken from the Leda document editor [Hi91] for the Oberon system and parallel the rastered font files.
  147.     Printing of elements is close to the process of displaying elements on the screen. After sending a prepare message, a print message is sent and the element in turn prints itself. (The print message contains the current page number which an element may use to produce page number dependent informations such as an index.)
  148.     PrintMsg = RECORD(Display.FrameMsg)
  149.         fnt: Fonts.Font;
  150.         col: SHORTINT;
  151.         unit: LONGINT;    (*units per device pixel*)
  152.         pno: INTEGER;    (*page number*)
  153.         X0, Y0: INTEGER    (*absolute bottom-left coordinate in device space*)
  154.     END;    
  155. Part 2 - Elements : Extensions to the Write System
  156. The second part of this report concentrates on the main theme of an extensible system: It's extensions. For the Write system, extensions are called elements. As elements are objects in the sense of object-oriented programming, the first sections study the standard structure of such objects and the messages that need be understood. Building on this general view, a series of sections introduces successively more advanced element features together with available elements that make use of these features. For each of the discussed elements, a new problem and its solution is shown. Thereby it is demonstrated that the complexity introduced by an element is proportional to the level of functionality provided by that element.
  157. 2.1  The Structure of an Element Implemention
  158. Elements are the key to the Write system's extensibility. Using the object-oriented style of programming, i.e. by implementing elements as objects, the functionality of possible elements is roughly circumscribed by the set of messages sent to an element by the Write core system. This section describes the typical structure of a module implementing an element. Two sample implementations of Write elements are listed in full detail in the appendix. The messages sent by the core system are summarized in the next section.
  159.     An element's implementation is typically encapsulated in a single module, although, in principle, it could take more than one module. Also, one might want to package multiple, perhaps correlated elements into a single module. Without loss of generality, it is assumed that a single element is implemented in a single module. Such a module needs to implement three basic groups of functionality. First of all, it provides a set of commands allowing a user to at least insert a new element of the implemented class into a Write text. Secondly, it contains the message handling for all messages understood by the implemented elements. Thirdly, it contains the hook for Write's generic dynamic loading facility. Each of these three functions will be explained in the following.
  160.     The Oberon system uses the concept of commands to link arbitrary modules to flexible user interfaces. By issueing a command, the user forces the system to execute a (parameterless) procedure exported by a particular module. If necessary, the required module is loaded. In the Write framework, elements found in opened texts cause their implementing modules to be loaded. To insert new elements into a text, each module is expected to provide appropriate commands. A typical insertion command takes some parameters to produce a customized element of the desired class. The created element is than inserted at the current caret position.
  161.     Once an element has been inserted into a Write text it is a potential receiver of messages sent by the Write core system, or sent by one of the participating element implementations. The used message handling framework corresponds closely to that used for the Oberon viewer system [WiGu88]. Messages are extensible records [Wi88a] dispatched to objects via object specific handlers. A handler uses a series of type tests applied to the message record to determine which action to take. It is possible for an object to ignore messages or to delegate them to some other object or module. This instance-centered style [Wi89] incorporates little conceptual overhead and opens designs with great flexibility. However, often the flexibility is more than one actually wants and the price paid in terms of weaker specifications and less clear system structure may be considered too high. While in princpiple, Write could adopt a class-centered style for its extension space (i.e. the element interface), this has not been done for the current version. The main rational for staying with the object-centered style is conformance with the Oberon system: It was a major design goal of Write to aim at a rather seamless integration with existing Oberon applications.
  162.     The handler associated with an element dispatches received messages using type tests. In order to be able to extend an element implementation, the handler should be exported. For each message to be handled, a handler should call a separately exported procedure (a handler component), as this enables an extension to selectively call components of the overriden handler. Cf. appendix C for examples on element handler implementations. The following table summarizes the standard messages sent to elements by the Write system.
  163. Table 2. Summary of Standard Messages to Write elements.
  164. 2.2  Temporary Elements - Error Elements
  165. The minimal form of a Write element are temporary elements. Such elements are never stored and hence need neither methods to load or store themselves nor a generic allocation mechanism. Typically, temporary elements do not print either and thus understand little more than Draw and perhaps Prepare messages. Upon storing a text all temporary elements vanish automatically.
  166.     An example for temporary elements are ErrorElems as listed in full source in appendix C. When using Write to edit programs, error elements can be used to translate error lists produced by a compiler into special marking elements within the source text. Since elements move when editing text, it is possible to find all error locations even when the text has been edited. By clicking on an error element, the element expands itself and displays the corresponding error message. (Cf. sections 2.4 and 2.6.)
  167. MODULE DoubleBug;
  168.     PROCEDURE One;
  169.     END Two; 
  170. BEGIN Three
  171. DoubleBug.
  172. The following list summarizes the commands implemented by ErrorElems. The same format will be used throughout this report. For the conventions used to describe the parameters taken, refer to appendix A.
  173. command    explicit parameters    implicit parameters
  174. ErrorElems.Mark    ("^" | error-list)    marked viewer
  175. Takes the list of errors produced by a compiler (position, error code pairs) and inserts error marks into the marked text. If error marks were already present in that text, they are removed beforehand.
  176. ErrorElems.Unmark        marked viewer
  177. Remove all error elements from the marked text.
  178. ErrorElems.LocateNext        marked viewer, caret
  179. Locate the next error element starting the search at the caret position. If no caret is set in the marked viewer, the first error element is located.
  180. 2.3  Static Elements - Chart Elements    (ChartElems due to C. Pfister)
  181. When adding interpretation of standard messages for loading, storing, and printing an element, the straightforward class of so called static elements results. Such static elements are very close to plain characters in that their size and content are fixed at insertion time.
  182.     A typical static element implementation are ChartElems. Taking a list of specially marked numbers, ChartElems generates a series of simple rectangular elements arranged as a horizontal or vertical bar chart. Each of the chart elements displays (and prints) itself as a frame scaled to the value consumed when creating it.
  183. Figure 5. Horizontal and Vertical Bar Charts.
  184. command    explicit parameters    implicit parameters
  185. ChartElems.Create    ("width" | "height") number    selection
  186. Starting from the most recent selection, the text is scanned until a "~" is found. All numbers prefixed with a backslash are replaced by a chart element. Everything else remains untouched.    
  187. 2.4  Adaptive Elements - Line Elements
  188. For certain elements it is meaningful to automatically adapt to the current environment when getting displayed or printed. For example, an element may have different looks when displayed and when printed. This can be used to implement elements like bookmarks that are completely suppressed when printed. Also, an element may automatically adapt to the available line height, line width, or remaining space on a partially casted line. This latter feature is used by LineElems to provide simple horizontal or vertical lines.
  189.     An element adapts to the display or printing environment by interpreting the prepare message. The message contains information about the space already consumed in the currently casted line, whether the element is about to be printed or displayed, and if it is going to be printed, on which page that will happen. Thus an element may even adapt to the page number during the print process. Furthermore, an element receiving a prepare message can retrieve the parc valid for its position and thereby change its geometry depending on all parc attributes, like line width and tabulator settings. The following table gives some examples.
  190. width    height    first tab    second tab
  191. auto    1    
  192. auto    5    
  193. auto    auto    
  194. tab    5    
  195. tab    auto    
  196. 1    auto    
  197. 5    auto    
  198. 20    auto    
  199. Table 3. Various Line Elements.
  200. command    explicit parameters    implicit parameters
  201. LineElems.Insert    ("^" | (("auto" | "tab" | W) ("auto" | H)) )    caret
  202. Inserts a line of given or automatically adopted width and height into a Write text. Width and height may be set in units of 1/10 mm. Otherwise, the width may be set to extend to the end of the current line ("auto") or to the next tabulator position ("tab"). The height may be set to equal the paragraph's line height (determined by the parc's line attribute).
  203. 2.5  Active Elements - Clock Elements, Icon Elements    (Clock-, IconElems due to R. Griesemer)
  204. Besides passively adapting to the display or print environment, elements may be active in the sense that some text external events like the passing of time affect a displayed element. Two examples (which of course are not visibly active in this printed report) are clock and icon elements. The basic mechanism is the separation of model and view, where the actual element floating in the text takes over the role of a view to some connected model. In the case of clock and icon elements, the external model is little more than the real-time clock, i.e. the absolute time. As shown below, clock elements simulate an analog clock, while icon elements display kind of an icon sequence film. (As an added feature, icon elements act as screen savers when being clicked on.)
  205. Figure 6. A Clock and an Icon Element.
  206. The model needs to notify its views when some update needs to be performed. As the model (e.g. the clock-polling task) has no control over the location and number of currently visible views (i.e. displayed elements), a special message broadcast mechanism is used. The model broadcasts a notify message to all viewers (using the Oberon viewer system's broadcast) which delegate it to their subframes. If a WriteFrames frame receives such a notify message it adds its own identity to the message and broadcasts it to all elements in the visible range (using the procedure WriteTexts.Broadcast). This is done using the range broadcast mechanism of WriteTexts.
  207.     WriteFrames.NotifyMsg = RECORD(Display.FrameMsg)
  208.         unit: LONGINT;
  209.         frame: Frame
  210.     END;
  211. A view (an active element) receiving a notify message in turn asks the containing frame for its current location, and whether it is fully visible. (Remember that elements are completely clipped when not fully visible.) This is done by calling WriteFrames.LocateElem. If the receiving element indeed is fully visible, it queries its model and thereby learns about the new state to be displayed. In turn, the element may redraw (part of) itself. If an element needs to resize itself as a reaction to the changes, it does change its geometry and state immediately and triggers a full redraw within a recasted environment by notifying the containing text of its change. To do so, the element calls WriteTexts.ChangedElem. In the former case, i.e. when redrawing in place, the element should use inverse mode drawing operations, only, as the element may be selected and therefore displayed in reverse video. In the latter case, to avoid multiple view updates it must be checked whether the model has really changed: The notify message might be received by multiple views showing the same element, but the first one will update all others by calling WriteTexts.ChangedElem.
  212.     Storing an active element and its model requires additional thought. As several elements might relate to the same model, multiple storage of the model should be avoided and the N:1 relation should be re-established at load-time. To distinguish between elements receiving a store message during a single store action, and elements receiving a store message in consecutive stores, variable WriteTexts.storeTime can be used. It is set to Oberon.Time() at the beginning of each call to WriteTexts.Store. This can be used to reset a model dictionary used during element storage whenever a timestamp associated with the dictionary fails to match WriteTexts.storeTime.
  213. command    explicit parameters    implicit parameters
  214. ClockElems.Insert    [number]    caret
  215. Insert a clock (with radius given in pixels, default is 32, minimum is 12) at the caret position.
  216. IconElems.Insert        caret
  217. Inserts a new Icon Element at the caret position.
  218. 2.6  Interactive Elements - Popup and Style Elements    (PopupElems due to M. Franz)
  219. Several of the elements discussed so far already added some degree of user interaction by interpreting certain mouse clicks. This can be generalized to fully interactive elements as a WriteFrames frame delegates all mouse clicks that happen within a displayed and fully visible element to that element by sending a track message. By tracking the mouse and thereby consuming the mouse event, an element can handle mouse click combinations. If it ignores the track message or a certain mouse click combination, the tracking is performed by the enclosing frame.
  220.     From a user's point of view, nested editable objects add a nonneglectable amount of complexity. If floating elements support full editing in place, the user has to distinguish three different kinds of mouse clicks interpreted in potentially very narrow or even overlapping screen areas. First of all, certain mouse clicks are on the level of the containing frame, and ask for placing the caret or selecting some text stretch. Secondly, other mouse operations are required to operate on a floating element, like resizing it. Thirdly, again other mouse operations are meant to operate within an element, such as editing its contents. Things get even worse if arbitrary nesting of interactive objects is to be allowed.
  221.     Within Write the convention is adopted not to support direct editing of nested objects, i.e. elements floating within a text. This is not a limitation of the framework, as each element can easily implement in-place editing if desired. However, experience has shown that such in-place operations get complex and unhandy, especially for the casual user of the system. Hence, the much simpler method is preferred to open a separate editing viewer when clicking on an element. Usually the middle mouse button is used as it has no obvious semantics for floating elements, anyway.
  222.     Two rather different examples for such interactive elements are PopupElems and StyleElems. PopupElems implement popup menus containing arbitrary and editable Oberon commands. Using the middle mouse button, a popup menu is opened; a middle-right interclick opens a viewer displaying the commands to be edited. StyleElems extend standard parcs (which already are interactive) by giving them a name: All style parcs within a text that have the same name change synchronously. When copying a style parc from one text to another, the parc will be copied as is, if the target text does not already contain a style of that name. If it does, the copied parc will be changed to conform to the existing style. Note that StyleElems give an example on how to extend extensions (in this case parcs), while all other examples given in this report are extensions of the element base type.
  223. Figure 7. A Popup Element.
  224. Figure 8. A Style Element named "Chapter Heading".
  225. command    explicit parameters    implicit parameters
  226. PopupElems.Insert    string    caret
  227. Inserts a new popup element with an initially empty menu. The menu may be edited by opening an editor using a middle-right interclick.
  228. StyleElems.Insert    ("^" | name | string)    caret
  229. Insert a new style parc with the given name at the caret position. A quoted string may be used to assign names consisting of multiple words. If the target text already contains a style with the given name, the inserted parc will adopt that existing style. Otherwise, the style of the target text's default parc will be used.
  230. StyleElems.Rename    ("^" | name | string)    selected parc
  231. Renames the selected parc to the given name. If the text already contains a style with the target name, the selected parc will be changed to conform to that existing style.
  232. 2.7  Wrapper Elements - Graphics and Picture Elements    (PictElems due to H. Marais and K. Rege)
  233. Having interactive elements, a straightforward consequence is to add elements that enclose some existing editable object class not originally designed to be encapsulated in an element. Such elements are called wrappers, as they merely provide the glue required to "wrap" some object and let it appear as an element. Here, the convention developed above, that it is preferable to edit elements in a separate viewer instead of in-place, gets a new strengthening argument. As the existing object classes necessarily do not know about elements, utility commands developed to edit them will not work as they cannot be applied to a floating element. However, the separate editing viewer opened for such elements behaves like the standard viewer used for the corresponding object class opened and is therefore directly accepted by such commands.
  234.     Currently, there are two wrapper elements available: GraphicElems and PictElems, wrapping the standard Oberon line drawing system Draw and the picture editing system Paint, respectively. GraphicElems have already been used extensively throughout this report to add all kind of illustrations.
  235. Figure 9. A Picture Element.
  236. command    explicit parameters    implicit parameters
  237. GraphicElems.Insert    ("^" | "*" | name)    caret, marked viewer
  238. Inserts a graphics element sized to show the graph stored in graphics file "name". If an open graphics viewer is marked, the command can be used to insert all or part of a displayed graph: if a selection exists in the marked graphics viewer's graph, only the selection is copied into the graphics element.
  239. PictElems.Insert    name ["scaled"]    caret
  240. Inserts the named picture at the caret position. If "scaled" is added, the picture will be scaled to fit into a 3 by 3 cm area. Otherwise, the picture will be displayed in full size. The middle mouse button may be used to open a Paint editing viewer. When clicking into the lower right corner, a scaled picture can be resized.
  241. 2.8  Structuring Elements - Fold Elements    (FoldElems due to H. M
  242. ssenb
  243. Like parcs, elements may be conceived that operate on their environment. While formatting modes must be known to the formatting instance, i.e. the frame and the print mechanism, other structuring effects can be introduced freely. However, such structures should be defined implicitly as the stretch between certain elements floating in a text, as editing operations cannot be constrained and hence consistency of explicit structures cannot be guaranteed.
  244.     A rather mighty example are fold elements which allow to structure a text into hierarchically folded segments. Folds can be expanded or collapsed at will. The idea is to support zooming into areas of interest while at the same time preserving an overview picture when collapsing folds. There are two different kinds of fold elements: Fold opening and closing ones. To avoid explicit structures, the opening elements contain all the relevant information attached to a fold, while the closing elements are matched on demand using a simple syntactic rule. Missing closing elements are catched by the text's end, superfluous closing elements are ignored. Thus, the user can freely edit the folded text, and inconsistent states are not possible.
  245.     Folding a text stretch causes its replacement by another, usually shorter or empty text stretch. Unfolding a folded stretch reveils the original text stretch while hiding its replacement. It is possible to unfold or to fold all folds existing in a text. Also, a (partially) folded text may be compiled as if it where fully unfolded using a utility module FoldComp. FoldComp also includes the functionality of ErrorElems, as defined elsewhere in this report, but uses a different file Oberon.Errors to translate error codes into error messages. Oberon.Errors also contains the name of the compiler to be used by FoldComp.
  246. Figure 10. A Text containing Fold Elements.
  247. command    explicit parameters    implicit parameters
  248. FoldElems.Insert        selection
  249. Make selection foldable by inserting an appropriate pair of opening and closing fold elements before and after the selected text stretch, respectively.
  250. FoldElems.Expand        marked viewer
  251. Unfolds (expands) all folds contained in the marked text.
  252. FoldElems.Collapse        marked viewer
  253. Folds (collapses) all folds contained in the marked text.
  254. FoldElems.Marks     ("^" | "on" | "off")    marked viewer
  255. Make all fold elements in the marked text visible or invisible.
  256. FoldElems.Search        selection, marked viewer    
  257. Search for the selected text stretch in the (partially) folded marked text starting at the caret position. Does search within folded parts and opens folds if necessary to display found text stretches. If no selection is present, the last search pattern is used again. If no caret is present, the search starts at the text's beginning.
  258. FoldComp.Compile    ("*" [compiler-options])    marked viewer
  259. Compile the marked text containing folds. The compiler is started on the fully expanded view of the text.
  260. FoldComp.ShowError        caret, marked viewer
  261. Show next error after the caret position. If no caret is present, the first error in the marked text is displayed.
  262. 2.9  Formatting Elements - Table Elements
  263. The elements discussed so far added new object types embedded in Write elements. By introducing Write texts as element contents, recursion and the potential of recursive constructors can be implemented. By doing so, a Write element can incorporate its own world of text representation and formatting to serve special purposes. A quite powerful example that makes use of recursively embedded Write texts are table elements. A table element automatically translates a primitive data format into tables, examples for which can be found all over this report. The last generic element class introduced in this report is intentionally exemplified using a rather heavy-weight extension: The quite mighty table elements demonstrate the potential available within Write's extension scheme.
  264.     Starting from a defining Write text containing arbitrary strings separated by tabulator and carriage-return characters, a table is constructed. Tabulators separate columns, carriage-returns separate rows. The separated strings form the contents of table cells. Each such string may in turn contain arbitrary Write elements and especially it may contain nested tables. The defining text is bound to a table element and can be edited by clicking on a table with the middle mouse button. In return, a standard Write viewer is opened containing an update command in its menu bar. The syntax of the defining text is as follows, table options will be defined later.
  265. table = {option} "/table" {line}.
  266. line = cell {TAB cell} CR.
  267. cell = ["#" | "&"] <string of characters not containing TAB or CR>.
  268. A string starting with a digit, a period (.), or a sign (+ or -) is considered numeric, otherwise it is considered nonnumeric. A number sign (#) prefixing a string is suppressed and enforces numerical interpretation; likewise, an ampersand (&) prefixing a string is suppressed and enforces nonnumerical interpretation. The default table formatting causes all cells in the first column to be formatted left flush. In all other columns, the default format for numeric cells is period-aligned and that for nonnumeric cells is centered. All cells in a row are by default aligned to a common base line. Also, the default format causes the table to be framed and separating lines between rows and columns to be drawn. Finally, for a table with more than two rows or two columns the first row or column separatation line is drawn using two parallel lines, respectively. Most of the default formats can be changed using the provided option set.
  269.     For table elements the printing dilemma discussed in section 1.5 needs to be reconsidered. Following the strategy developed for Write texts, a table would be recasted when printing. However, one of the primary attributes of a table are its measures, i.e. the space taken for individual cells of the table and the arrangement of the cells with respect to each other. It is not acceptable that a table gets completely redimensioned when printed. Hence, for table elements a different printing strategy has been choosen: the displayed table on the screen follows - down to the character level - the metrics of the printer fonts. The resulting screen image looks a bit distorted, however, the displayed measures correspond as closely as possible to the printed ones.
  270. Table 4. Column Formatting.
  271. Table 5. Row Formatting.
  272. Figure 11. Table Lines and Cell Margins.
  273. Figure 12. Frame Formatting.
  274. Figure 13. A Complicated Composed Table.
  275. command    explicit parameters    implicit parameters
  276. TableElems.Insert    ("^" | name)    caret
  277. Inserts a table using the named text as defining text. If no such text exists, an empty table is inserted.
  278. Table 6. Overview of Formatting Options.
  279. 2.10  Special Purpose Elements: Building an Application using Write    (Example due to B.Heeb)
  280. Beyond extending Write's usability as a text editor by providing new elements, Write can be used as a framework for rather specific applications. By decomposing the visual elements of an application into fragments and implementing these fragments as Write elements, a large part of the code typically required to implement interactive applications can be avoided. By interpreting a Write text as an application specific "panel" the available editing operations can be used to freely arrange the application's visible components. To close the second part of this report, an example of such an application is given.
  281.     To interactively simulate electronic circuits different signals found in the circuit are to be displayed as trace diagrams showing the change of a signal over simulation time. The Debora simulator is part of an ongoing research project. It uses a special Write element per trace. When displaying itself a trace element scans the name written in front of it to dynamically associate a simulated signal to be displayed. Furthermore, a trace element uses the tab setting of the valid parc to derive the time unit, i.e. the scale at which to display the trace. In turn, the user can freely arrange traces annotated with their natural signal names and select the wished time base. Trace elements are also interactive and can serve as input to a simulation, too, as it is possible to edit a trace in several ways.
  282.     As this section is not meant to be the documentation of the Debora toolset still under development, no further details of trace elements will be looked at. However, it should be noted that the mere implementation of trace elements is sufficient to construct an application that allows free composition of signal traces together with arbitrary Write texts as annotations. The resulting active texts can be frozen and printed at any time.
  283. TrEmpty    
  284. TrCrc    
  285. DrivEn    
  286. Figure 14. Debora Simulation Elements.
  287. Summary and Conclusions
  288. The delicate balance between a rich basis for arbitrary extensions and carefully chosen restrictions to certain simple concepts determines the usefulness of an extensible system. It should be clearly noted that the unrestricted extension potential tends to be overshadowed by the massive burden that every actual extensions needs to carry, even if it is meant to perform a rather straightforward task. A compromise is necessary to enable the common case of simple extensions at a low implementational and conceptual price.
  289.     This report concentrated on the extensible text editor Write which attempts to limit extensions to a single atomic concept: The character. As texts are naturally thought of as a (formatted) sequence of (attributed) characters, this model is quite intuitive in capturing the idea of extending the attribute space of a character. However, some further techniques are required to capture the formatting space of a text. In Write both concepts have been coerced by introducing special characters, called parcs (paragraph controls), that implicitly define formatting attributes over character ranges. In this sense, parcs may be compared to rulers found in many other text editors. Indeed, at least the LisaWrite editor [Wi83] and its successors (like MacWrite) treated rulers nearly as characters in that all standard character editing operations can be applied to rulers.
  290.     It is very important that parcs are realized without adding a new structure to the system. Instead, the existing structure enabling all element extensions of the editor, can be queried to find the parc responsible for a certain position in a Write text. Likewise, all other extensions illustrated in this report work without any additional structures superimposed on the text carrying the extensions. In all cases, required information is derived from the implicit relation of elements positioned in a text. The most advanced example for this technique are folding elements which by their nature always act in pairs. Instead of linking a pair of elements, a fold element seeks its partner element on demand. If the elements would contain links, maintaining consistency would lead to necessary modifications or restrictions of the user's editing model. Consider the case where the user deletes a fold element but not its partner element. Or even worse: One of the two elements is moved to a different text but not the other. To cope with such cases in the case of an explicit structure (e.g. pointers between elements not known to Write), the system would have to either restrict the edit operations, or the edited elements need to be notified in order to repair the text, e.g. to make it consistent again.
  291.     The large number and variety of the existing element extensions discussed in this report serve to strengthen the point. The simple model does indeed enable a quite rich world of extensions. The simplicity of two sample implementations of element extensions given in the appendix clearly demonstrates that it is also easy to add new extensions. The latter argument is further supported by the large number of different authors that already implemented Write extensions. Finally, the set of nontrivial extensions presented (like popup, fold, graphics, picture, or table elements) shows that the Write extension world is not limited to toy examples. Last but not least, the approach to restricted extensibility led to a relatively simple core system. The major gain is its robustness: After the first release no destructive bugs have been found. Also, most bugs found corresponded to the frame update mechanism which is indeed one of the most complex parts.
  292.     Write has been implemented in the Oberon system using the Oberon language. Both proved to be valuable tools to rapidly design and implement a functioning and useful application. However, certain aspects of the Oberon system hinder a free extension even at levels as those of the Write system. Especially, the large number of statically bound procedural interfaces to central components make certain extensions inherently unsafe. A class-centered style based on a dynamic binding of procedures would help; the ongoing Ethos project investigates this further as a general principle of structuring an operating system [Szy90a, b].
  293.     The reader might notice that this report is kind of an existence proof of Write, as it has been written and printed using solely Write and all of the mentioned Write elements. The whole report has been handled as a single Write file with good performance on a Ceres-2 machine (25MHz National Semiconductor 32532 Processor). It contains 16635 words, 109393 chars, 180 elements, and takes 156707 bytes to store. Appendix B adds some measures on the system's code size.
  294. Acknowledgements
  295. I would like to express my deep appreciation to the large number of early users of the Write system. Providing a useful system as the result of a short-termed project is a challange and requires a certain amount of "guinea-pigs" to test results on. (However, the fact that everybody stayed alive and that many of the Write users actually became implementors by adding new elements is quite encouraging.) Also, I would like to thank H. M
  296. ssenb
  297. ck and J. Templ for careful reading and many valuable comments on this report.
  298. References
  299. Appendix A - Write User Manual
  300. Write is an extensible text editor for the Oberon system. It is based on a few simple concepts and aims at typical writing tasks, such as memos and reports. It does not try to support a document model, and it is not fully "wysiwyg". To arrive at a rather simple implementation, the few concepts provided have been carried out with all consequences. This should be kept in mind before considering a certain behaviour of the editor to be a "bug". The following tutorial assumes that the reader already knows how to use the Oberon system, especially, how to handle viewers, files, and commands. Many important Write extensions, called elements, are documented in the second part of this report.
  301. Principles
  302. A Write text models a sequence of characters. Besides standard characters, Write supports special user extensible characters. Such characters, called elements, float in the text just as ordinary characters do. Typical elements support the integration of graphics and the like into texts. A standard extension of elements allows for separating the text into paragraphs. Such elements are called paragraph controls or parc for short. A parc defines the paragraph attributes for all characters following it up to the next parc or the end of the text.
  303.     Mouse commands are interpreted by Write just as defined by Edit. Exceptions to that occur when clicking on an element. Here, the element is free to consume (and act on) the mouse click, overriding the standard behaviour. This is used by parcs to support interactive setting of paragraph attributes.
  304.     Insertion of new characters into the text requires setting the caret left to the character before which to insert, or to the end of the text. Selecting a stretch of the text corresponds to selecting a range of characters. The visible selection extends from the beginning of the first selected character to the beginning of the first character behind the selection or the end of the text. Hence, selections always reflect the exact area which is affected when changing or deleting.
  305. Editing
  306. Basic editing functionality is just as in Edit. This includes scrolling, setting the caret, selecting text, inserting, copying, and deleting text stretches, and changing attributes of characters. The cursor left/right keys move the caret. To support editing indented text (like programs), the Linefeed key can be used instead of Carriage-Return. This will indent the next line to the same level as the previous one. The cursor left/right keys will shift indented text if it is selected in the focus viewer with the caret invisible. Furthermore, when extending a selection over two consecutive viewers, Write adds visual feedback. This avoids the danger of extending a selection by mistake and then deleting or copying a far larger stretch of text than was intended. See below for a description of commands that further support basic editing.
  307.     In order to insert a parc into a text, place the caret at the appropriate position and press BREAK. This will copy the parc above the caret position (or the default parc if there is none above). A parc is displayed as a separation line corresponding to the width set for that paragraph. Above that separation line, one or two marks signal the formatting mode of the paragraph: a single mark at the very left, in the middle, or at the very right indicates left flush, centered, or right flush formatting, respectively. Two marks at both ends indicate block (fully justified) formatting. Below the separation line, set tabs are indicated by marks. Pressing Shift-BREAK inserts a parc which forces a page break (attribute break=before). Such parcs are displayed using a solid separation line.
  308.     A parc defines a special behaviour for middle-button mouse clicks. It has two separate sensitive areas. One above and one below the separation line. The following table shows how various middle mouse button clicks and interclicks affect the paragraph attributes bound to a parc. Again, see the commands below for further manipulations of parcs.
  309. Figure 15. Display of a Parc.
  310. Table 7. Interactive Parc Manipulation.
  311. Printing
  312. When printing a text, Write reformats individual paragraphs for the printer. Write preserves all user setable measures, while individual words may be placed differently. This allows for optimal display of texts on the screen, while at the same time fully exploiting the printer resolution. Hence, it should not be tried to affect the placing of individual words by inserting additional blanks or the like! Furthermore, Write ignores all empty space at the beginning of a page except when preceded by a parc with enforced page break attribute. White space in this sense are empty lines (a single blank makes a line non-empty!) and lead-space defined by a parc. Refer to the print command description below for details on how to initiate a printout.
  313.     In order to achieve good results, the Lm3 metrics files for the used font families should be available. For example, when using the fonts Syntax12, Math12, Syntax14, and LetterHead, the metrics files Syntax.Lm3.Fnt, Math.Lm3.Fnt, and LetterHead.Lm3.Fnt are required. If a metrics file for a used font is missing, the printer metrics are estimated using a simple heuristics based on the screen font metrics. Beware: this leads to at most draft quality of the printed text.
  314. Commands in the Viewer Menu
  315. Write.Search
  316. Takes the most recent selection as search argument. If the selection is older than the latest one used for Write.Search, the previously set search argument will be used. Write.Search starts searching at the current caret position, or at the beginning of the text, if no caret is set. When starting the search behind the last occurence of the search pattern, the search will automatically wrap around and start over again at the beginning of the text.
  317. Write.Replace
  318. Takes the most recent selection as replacement argument. If the selection is older than the latest one used for Write.Replace, the previously set replacement argument will be used. Write.Replace verifies that the pattern right to the current caret position matches the current search argument. If so, it is replaced and Write.Replace automatically searches for the next occurence. Write.Replace does not wrap around when searching.
  319. Write.ReplaceAll
  320. To make Write.ReplaceAll usable, the separating space in the menu has to be deleted. Write.ReplaceAll operates much the same way as Write.Replace does. Additionally, the replacing process is carried on to the end of the text. Write.ReplaceAll does not wrap around when searching.
  321. Write.Store
  322. Stores the text. After completing, Write.Store writes out the number of characters in the text. Whenever the text displayed in a Write viewer has been changed but not yet stored, the menu bar contains an exclamation mark following the Write.Store command. If a text is stored under a name that already exists, the existing file X is renamed to X.Bak.
  323. Commands in the Write Tool
  324. The following commands are supported by Write. Generally, the standard Write tool contains several examples on how to use the Write commands, most of which should be self-explaining. For each command, the parameters directly following the command are given. Furthermore, most of the commands take further implicit parameters, like the current selection, the current focus (the caret), and the selection in the currently marked viewer. Such parameters are also listed below. Whenever the marked viewer (or the selection in its body frame) is taken as a parameter, the viewer is expected to display a Write text. The selection symbol "^" adds a level of indirection by taking the explicit parameters of the command from the current selection. Explicit parameters may be names (sequences of characters starting with a letter, followed by letters, digits, or periods), strings (sequence of characters enclosed by quotes), or numbers (sequences of characters starting with a minus or a digit, followed by digits).
  325. command    explicit parameters    implicit parameters
  326. Write.Open    ("^" | name)
  327. Opens a Write viewer displaying the named text.
  328. Write.SysOpen    ("^" | name)
  329. Opens a Write viewer in the system track displaying the named text. The default formatting uses a reduced width and a regular tabulator setting every 5mm.
  330. Write.Store    name    marked viewer
  331. Takes the text displayed in the marked viewer and stores it under the given name. The total number of bytes taken by the created file is written to the log.
  332. Write.Print    server-name ("^" | "*" {option} | {name {option}} "~")
  333. Prints a list of Write texts. The print options are explained in a separate table below. Unless specified otherwise (using the /p option), Write.Print will assign consecutive page numbers to all texts printed with a single print command. As feedback, Write.Print writes the name and the number of copies of each printed text to the system log. Also, a period (.) is written to the log for each page sent to the printer and an apostrophe (') is written for each page formatted but skipped (due to an /s option).
  334. Write.Recall        caret
  335. Inserts the most recently deleted text stretch at the current caret position. Write.Recall can be used repeatedly to insert a deleted portion at several places. (Edit.Recall does nothing when applied to a Write text.)
  336. Write.InsertParc        caret
  337. Inserts a new default parc at the current caret position.
  338. Write.Locate    ("^" | number)    marked viewer
  339. Locates a character position and makes it visible in the marked viewer. (Do not use Edit.Locate to locate a position in a Write text!)
  340. Write.SelectParc        caret
  341. Make visible the parc corresponding to the paragraph containing the caret and select it.
  342. Write.ChangeFont    ("^" | name)    selection in marked viewer
  343. Change the font attribute of the selected characters in the marked viewer.
  344. Write.ChangeColor    ("^" | number)    selection in marked viewer
  345. Change the color attribute of the selected characters in the marked viewer. (Has no visible effect on monochrome monitors, on color monitors; 0 is the background and 15 the foreground color.)
  346. Write.ChangeOffset    ("^" | number)    selection in marked viewer
  347. Change the vertical offset attribute of the selected characters in the marked viewer (-128..127). The offset is specified in 1/64th of the font height of the affected character.
  348. Write.Set    ("^" | {attribute-name [values]})    selected parc in marked viewer
  349. Sets paragraph attributes bound to the selected parc. The paragraph attributes are explained in a separate table below.
  350. Write.Get    ("^" | attribute-name)    selected parc in marked viewer
  351. Gets paragraph attributes bound to the selected parc. The paragraph attributes are the same as for Write.Set. If no special paragraph attribute is selected, all attributes will be shown.
  352. Write.MakeDefault        selected parc in marked viewer
  353. Sets the default formatting of the marked text to the attributes of the selected parc.
  354. Write.ShowAliens        marked viewer
  355. Searches the marked text for undefined elements. Such elements occur, if a text is opened while some defining modules of extended elements are not available. Write displays undefined elements uniformly as an empty frame. Write.ShowAliens lists the position of such elements, as well as the names of the missing modules. Note that undefined elements can be selected, copied and deleted freely. As soon as the missing modules become available, it is sufficient to reopen affected texts and the elements will again behave as originally intended.
  356. Print Options
  357. By supplying print options to the Write.Print command, certain print conventions may be controlled. Options may be given in any order, but note that certain options may depend on values set by others. For example, "/p 5/s 5 7" starts page numbering with 5 and causes printing pages 5 to 7, i.e. the first three pages of the text. Hence, the range 5...7 of the select option corresponds to the origin set by the page number option ("s 5 7/p 5" has the same effect).
  358. Table 8. Print Options.
  359. Paragraph Attributes
  360. All numerical values are to be specified in 1/10 mm units (e.g. the value 150 corresponds to 1.5 cm). For lead and line space attributes, font names may be used. In this case, the font height is used to compute an optimal value.
  361. Table 9. Paragraph Attributes.
  362. The line spacing model assumes a minimal line height for each paragraph. If a line exceeds its minimal line height, it is automatically adjusted to avoid clutter between it and the line above it. If the line grid is turned on for that paragraph (default), the line is adjusted to an integer multiple of the minimal line height.
  363.     Advise: In order to reach a typographically sound layout, in most cases a single line height should be selected for all paragraphs of the text. To get good results when combining a font for body text with larger ones for titles and section headings and smaller ones for captions, it is useful to set the line height to the body font and turn the line grid on. In most cases, it is preferable to set line height and paragraph leadings using the name of the predominant font ("model font").
  364. Utility Module WriteTools
  365. A collection of utility commands most of which operate equally well on standard texts and Write texts. (The application to Leda texts is not recommended - the standard Leda Attributes Viewer should be used in conjunction with Leda.Search and Leda.Replace.)
  366. command    explicit parameters    implicit parameters
  367. WriteTools.GetAttr        selected character
  368. Get textual attributes of the selected character: ASCII code, font name, color, and vertical offset.
  369. WriteTools.IncSize    ("^" | number)    selection
  370. Increment font sizes in selected range (negative increments may be used to decrement sizes). If a computed font is not available, the old font is retained.
  371. WriteTools.ChangeSize    ("^" | {number "=>" number} "~")    selection
  372. Change font sizes absolutely within the selected range. If a computed font is not available, the old font is retained.
  373. WriteTools.ChangeFamily    ("^" | {name "=>" name} "~")    selection
  374. Change font families within the selected range. If a computed font is not available, the old font is retained.
  375. WriteTools.Change    ("^" | {name "=>" name} "~")    selection
  376. Change fonts within the selected range. If a font is not available, the old font is retained.
  377. WriteTools.Words    ("^" | {name} "~")
  378. Counts characters and words in the listed files.
  379. WriteTools.Elements    ("^" | {name} "~")
  380. Counts characters, words, and elements in the listed files.
  381. WriteTools.Elements    ("^" | {name} "~")
  382. Counts characters, words, and elements in the listed (Write) files.
  383. WriteTools.Cleanup    ("^" | {name} "~")
  384. Scans a Write text and removes alien and ill-sized elements. (Restricted in use to Write texts.)
  385. Appendix B - Interfaces of the Write Core Modules
  386. This appendix documents the interfaces of the Write core modules. For two complete and commented source listings of actual element implementations, cf. appendix C. For hints on how to implement Write elements in a way conforming to established conventions, see the second part of this report. The following table gives some impression on the code size of the Write system and some of its extensions. When comparing against the standard module triple Texts, TextFrames, and Edit, one has to take into account that WriteTexts heavily builds on module Texts, while WriteFrames and Write are fully independent. Also, the module WriteParcs could, in principle, be left out, leading to a version of Write with non-interactive parcs.
  387. Table 10. Size of Write Core System, Standard Text System, and Extensions.
  388. Module WriteTexts
  389. WriteTexts extends module Texts by adding the capability of arbitrary elements floating in the text just as ordinary characters do. In particular, WriteTexts extends the types Texts.Text and Texts.Buffer. The extension suffers from the impossibility of overriding procedures defined in Texts. When using WriteTexts, the following Texts procedures must be considered with special care. Procedures Texts.Delete, Texts.Insert, Texts.Append, Texts.Load, and Texts.Open must not be used on a WriteTexts text. Procedures Texts.Save, Texts.Recall, and Texts.Store have reduced semantics when applied to a WriteTexts text, as they ignore floating elements. All other procedures, especially those defined on Readers, Writers, and Scanners can be used freely. This includes procedure Texts.ChangeLooks to modify character attributes of a text range.
  390.     WriteTexts is not coupled to any particular device and especially to no particular resolution. Hence, all measures contained in elements and parcs are in device independent units. The used unit is defined as follows:
  391. Table 11. Pixel Resolution vs. Device Independent Units.
  392. Since the Ceres-Oberon screen fonts (*.Scn.Fnt files) are tuned for 91 dpi resolution, the scale factor used for other monitors attached to the Ceres (color monitors, Ceres-3 monitor) should be set to 10'000, anyway. Likewise, the existing printer fonts (*.Pr3.Fnt and *.Lm3.Fnt files) are tuned for 300 dpi resolution.
  393.     When changing an element or a parc inserted into a Write text, calling procedures WriteTexts.ChangedElem or WriteTexts.ChangedParc, respectively, initiates an appropriate change notification updating all existing views.
  394.     Variable WriteTexts.storeTime can be used to distinguish among different store actions when receiving a store message. This is useful when storing elements sharing a common model, as is explained for active elements in section 2.5.
  395. DEFINITION WriteTexts;
  396.     IMPORT
  397.          Display, Files, Fonts, Texts, Oberon;
  398.     CONST
  399.         replace = 0; insert = 1; delete = 2;
  400.         mm = 36000;    (*units per mm*)
  401.         ElemChar = 1CX;
  402.         MaxTabs = 32;
  403.         TextTag = 0F5X; ElemTag = 0F6X;
  404.     TYPE
  405.         Elem = POINTER TO ElemDesc;
  406.         Handler = PROCEDURE(E: Elem; VAR msg: Display.FrameMsg);
  407.         ElemDesc = RECORD
  408.             DX, W, H: LONGINT;    (*in units; DX >= W*)
  409.             handle: Handler;
  410.             temp: BOOLEAN    (*suppress storage of element*)
  411.         END;
  412.         Parc = POINTER TO ParcDesc;
  413.         ParcDesc = RECORD(ElemDesc)
  414.             first, left, width, lead, lsp, dsr: LONGINT;    (*in units*)
  415.             opts: SET;
  416.             nofTabs: INTEGER;
  417.             tab: ARRAY MaxTabs OF LONGINT    (*in units*)
  418.         END;
  419.         Buffer = POINTER TO BufferDesc;
  420.         BufferDesc = RECORD(Texts.BufDesc) END;
  421.         Text = POINTER TO TextDesc;
  422.         TextDesc = RECORD(Texts.TextDesc)
  423.             defParc: Parc
  424.         END;
  425.         LoadMsg = RECORD(Display.FrameMsg)
  426.             r: Files.Rider    (*DX, W, H loaded automatically*)
  427.         END;
  428.         StoreMsg = RECORD(Display.FrameMsg)
  429.             r: Files.Rider    (*DX, W, H stored automatically*)
  430.         END;
  431.         CopyMsg = RECORD(Display.FrameMsg)
  432.             e: Elem    (*receipient should allocate e iff e = NIL*)
  433.         END;
  434.         DrawMsg = RECORD(Display.FrameMsg)
  435.             fnt: Fonts.Font;
  436.             col: SHORTINT;
  437.             unit: LONGINT;    (*units per device pixel*)
  438.             frame: Display.Frame;    (*containing frame in device space*)
  439.             X0, Y0: INTEGER    (*anchor in device space*)
  440.         END;
  441.         PrintMsg = RECORD(Display.FrameMsg)
  442.             fnt: Fonts.Font;
  443.             col: SHORTINT;
  444.             unit: LONGINT;    (*units per device pixel*)
  445.             pno: INTEGER;    (*page number*)
  446.             X0, Y0: INTEGER    (*anchor in device space*)
  447.         END;
  448.         AllocPar = POINTER TO AllocParDesc;
  449.         AllocParDesc = RECORD(Oberon.ParRec)
  450.             e: Elem    (*receipient should allocate element e and install handler*)
  451.         END;
  452.         storeTime: LONGINT;    (*timestamp of most recent Store*)
  453.     (* Elements *)
  454.     PROCEDURE OpenElem(E: Elem; handle: Handler; dx, w, h: LONGINT);
  455.         (*forces dx >= w, e.temp = FALSE*)
  456.     PROCEDURE CopyElem(SE: Elem; VAR DE: Elem);
  457.         (*returns copy of SE in DE using CopyMsg; forced copying of DX, W, H, handle, and temp*)
  458.     PROCEDURE ElemClass(E: Elem; VAR unknown: BOOLEAN; VAR mod: ARRAY OF CHAR);
  459.         (*unknown => mod valid*)
  460.     PROCEDURE ElemBase(E: Elem): Text;
  461.         (*returns NIL if E is not within a text*)
  462.     PROCEDURE ElemPos(E: Elem): LONGINT;
  463.         (*returns -1 if E is not within a text*)
  464.     PROCEDURE ElemSucc(E: Elem): Elem;
  465.         (*returns NIL if no successor or not within a text*)
  466.     (* Parcs - Paragraph Controls *)
  467.     PROCEDURE ParcBefore(T: Text; pos: LONGINT): Parc;
  468.         (*returns T.defParc if none found*)
  469.     PROCEDURE ParcExtent(P: Parc): LONGINT;
  470.         (*parc P extends over range [ElemPos(P), ParcExtent(P) )*)
  471.     PROCEDURE LoadParc(VAR r: Files.Rider; P: Parc);
  472.         (*load parc attributes*)
  473.     PROCEDURE StoreParc(VAR r: Files.Rider; P: Parc);
  474.         (*store parc attributes*)
  475.     PROCEDURE CopyParc(SP, DP: Parc);
  476.         (*copy parc specific fields from SP to DP*)
  477.     PROCEDURE HandleParc(E: Elem; VAR msg: Display.FrameMsg);
  478.         (*primitive parc handler, calls LoadParc, StoreParc, and CopyParc*)
  479.     PROCEDURE AllocParc;
  480.         (*allocate plain parc for loading*)
  481.     (* Buffers *)
  482.     PROCEDURE OpenBuf(B: Buffer);
  483.         (*prepare & clear B*)
  484.     PROCEDURE CopyBuf(SB: Buffer; VAR DB: Buffer);
  485.         (*allocates DB if set to NIL; DB := copy(SB); SB unmodified*)
  486.     PROCEDURE SaveBuf(T: Texts.Text; beg, end: LONGINT; VAR B: Buffer);
  487.         (*allocates B if set to NIL; B := copy(T[beg, end])*)
  488.     PROCEDURE AppendBuf(SB, DB: Buffer);
  489.         (*DB := DB + copy(SB); SB := empty*)
  490.     PROCEDURE RecallBuf(B: Buffer);
  491.         (*B is copy of last deleted stretch*)
  492.     (* Texts *)
  493.     PROCEDURE Delete(T: Text; beg, end: LONGINT);
  494.     PROCEDURE InsertElem(T: Text; pos: LONGINT; E: Elem);
  495.     PROCEDURE Insert(T: Text; pos: LONGINT; B: Texts.Buffer);
  496.     PROCEDURE AppendElem(T: Text; E: Elem);
  497.     PROCEDURE Append(T: Text; B: Texts.Buffer);
  498.     PROCEDURE ElemAt(T: Text; pos: LONGINT): Elem;
  499.         (*returns NIL if not found*)
  500.     PROCEDURE FirstElem(T: Text): Elem;
  501.         (*returns NIL if no elems in T*)
  502.     PROCEDURE Broadcast(T: Text; beg, end: LONGINT; VAR msg: Display.FrameMsg);
  503.         (*broadcast to all elems in range [beg, end) of T*)
  504.     PROCEDURE ChangedElem(E: Elem);
  505.         (*notify about replacement of range [ElemPos(E), ElemPos(E)+1)*)
  506.     PROCEDURE ChangedParc(P: Parc);
  507.         (*notify about replacement of range [ElemPos(P), ParcExtent(P) )*)
  508.     (* Text Files *)
  509.     PROCEDURE Load(T: Text; f: Files.File; pos: LONGINT; defParc: Parc; VAR len: LONGINT);
  510.         (*load Texts or WriteTexts block at (f, pos) into T; use defParc for Texts blocks;
  511.         returns block length in len*)
  512.     PROCEDURE Store(T: Text; f: Files.File; pos: LONGINT; VAR len: LONGINT);
  513.         (*store T as WriteTexts block at (f, pos); returns block length in len*)
  514.     PROCEDURE Open(T: Text; name: ARRAY OF CHAR; defParc: Parc);
  515.         (*open T by trying to load (Files.Old(name), 0); can load plain-ascii files;
  516.         creates empty text otherwise*)
  517. END WriteTexts.
  518. Module WriteFrames
  519. WriteFrames extends module TextFrames by adding numerous formatting and editing capabilities. Care must be taken when using WriteFrames, as the procedures of module TextFrames must not be applied to a WriteTexts text. The most visible side effect is the non-functioning of command Edit.Locate which results in a trap (without doing any harm to the Write frame or its text).
  520.     WriteFrames contains the screen formatter. It defines a special prepare message (WriteFrames.PrepareMsg) sent to elements about to be displayed. This allows for adapting to remaining space on a line and the like. Hence, an element is allowed to change its bounding box upon receipt of a prepare message.
  521.     WriteFrames tries to delegate mouse clicks to elements hit by the cursor. This is done by sending a WriteFrames.TrackMsg. By tracking the mouse until all mouse buttons have been released again, the element can selectively consume mouse clicks. It is strongly recommended to restrict the set of consumed mouse clicks to middle button clicks and interclicks, i.e. the command click combinations. These are undefined for elements, anyway. Consuming left or right button clicks or interclicks causes interference with the caret and selection controlling clicks interpreted by Write frames.
  522.     It is possible to attach elements to some external model, i.e. to use elements as views showing some shared model. To update these views, a model change should cause a notification. For this purpose, a special WriteFrames.NotifyMsg is provided, which is broadcasted by a Write frame to all elements in the frame's visible range. An element receiving a notification message should call WriteFrames.LocateElem to find out whether it is truely visible (or clipped) and if so, what screen coordinates correspond to the element.
  523.     Opening a WriteFrame takes besides the handler to be used and the text to be displayed a bunch of additional parameters. For each of these a default variable is exported. The parameters define the border width defined around a displayed text in a Write frame (left, right, top, bot), the scroll bar width (barW, if barW > left : scroll bar is not displayed and not available), and a set of options. Currently, frame options WriteFrames.scrollOpt and WriteFrames.fontOpt are defined. If the scroll option is set, the frame scrolls automatically when user interactions (editing, moving the cursor, but not backward deleting) reach the frame bounds. Setting the font option causes the frame to take fonts from neighbouring characters when accepting keyboard input. (Both options are set by default.)
  524.     Although a WriteFrames.Frame is an extension of a TextFrames.Frame for backward compatibility reasons (e.g. the compiler accepts a WriteFrame as source), not all fields of a TextFrames.Frame record are supported by a WriteFrames frame. The fields col, mark, car, sel, carloc, selbeg, and selend are not used. The background color is always Display.black, caret and selection are reflected by the fields hasCar, hasSel, carLoc, selBeg, and selEnd. The field text is expected to refer to a WriteTexts text. To install a new text in an open Write frame proceed as follows. Set the text field of the frame descriptor to the new text. Set the trailer field to NIL. Call WriteFrames.Show(org), where org is the wished origin from where the new text is to be displayed. WriteFrames.Show automatically decrements org to begin at a valid line start position.
  525. DEFINITION WriteFrames;
  526.     IMPORT
  527.         Display, Fonts, Oberon, Texts, TextFrames, WriteTexts;
  528.     CONST
  529.         scrollOpt = 1; fontOpt = 2;    (*frame options*)
  530.         gridAdj = 0; leftAdj = 1; rightAdj = 2; pageBreak = 3;    (*parc options*)
  531.     TYPE
  532.         TextLine = POINTER TO TextLineDesc;
  533.         TextLineDesc = RECORD
  534.             next: TextLine;
  535.             eot: BOOLEAN;    (*contains end of text*)
  536.             w, h, dsr: INTEGER;    (*bounding box clipped to frame*)
  537.             org, len, span: LONGINT    (*len w/o; span w/ trailing CR or white space, if any*)
  538.         END;
  539.         Location = RECORD
  540.             org, pos: LONGINT;
  541.             x, y, dx, dy: INTEGER
  542.         END;
  543.         Frame = POINTER TO FrameDesc;
  544.         FrameDesc = RECORD(TextFrames.FrameDesc)
  545.             (*text, org, lsp, left, right, top, bot, markH, time inherited from TextFrames.FrameDesc*)
  546.             barW: INTEGER;    (*scroll bar width*)
  547.             hasCar, hasSel: BOOLEAN;    (*caret/selection present*)
  548.             opts: SET;
  549.             carLoc, selBeg, selEnd: Location;    (*locations of caret, selection begin, and end*)
  550.             trailer: TextLine    (*ring of line descriptors, points to trailer after last visible line*)
  551.         END;
  552.         UpdateMsg = TextFrames.UpdateMsg;
  553.         PrepareMsg = RECORD(Display.FrameMsg)
  554.             fnt: Fonts.Font;
  555.             col: SHORTINT;
  556.             unit: LONGINT;    (*units per device pixel*)
  557.             indent: LONGINT;    (*width already consumed in line, in units*)
  558.             printing: BOOLEAN;
  559.             pno: INTEGER    (*page number, valid if printing*)
  560.         END;
  561.         TrackMsg = RECORD(Oberon.InputMsg)
  562.             unit: LONGINT;    (*units per device pixel*)
  563.             frame: Frame;
  564.             X0, Y0: INTEGER    (*receiver origin in device space*)
  565.         END;
  566.         NotifyMsg = RECORD(Display.FrameMsg)
  567.             unit: LONGINT;    (*units per device pixel*)
  568.             frame: Frame    (*frame containing receiver*)
  569.         END;
  570.         defLeft, defRight, defTop, defBot, defBarW: INTEGER;
  571.         defOpts: SET;
  572.     (* locators *)
  573.     PROCEDURE BegOfLine(T: WriteTexts.Text; VAR pos: LONGINT; adjust: BOOLEAN);
  574.         (*set pos to the first character of the line containing pos;
  575.         adjust takes line wrap-around into accound*)
  576.     PROCEDURE LocatePos(F: Frame; pos: LONGINT; VAR loc: Location);
  577.         (*return location descriptor for position (F, pos)*)
  578.     PROCEDURE LocateLine(F: Frame; y: INTEGER; VAR loc: Location);
  579.         (*return location descriptor for line at (F, y); y in absolute screen coordinates*)
  580.     PROCEDURE LocateChar(F: Frame; x, y: INTEGER; VAR loc: Location);
  581.         (*return location descriptor for character at (F, x, y); x, y in absolute screen coordinates*)
  582.     PROCEDURE LocateWord(F: Frame; x, y: INTEGER; VAR loc: Location);
  583.         (*return location descriptor for word at (F, x, y); x, y in absolute screen coordinates*)
  584.     PROCEDURE LocateElem(F: Frame; E: WriteTexts.Elem; VAR visible: BOOLEAN;
  585.         VAR fnt: Fonts.Font; VAR col: SHORTINT; VAR X0, Y0: INTEGER);
  586.         (*return whether element E is visible in F; if so, return its font and color (fnt, col),
  587.         and base coordinates (X0, Y0)*)
  588.     PROCEDURE Pos(F: Frame; x, y: INTEGER): LONGINT;
  589.         (*return position in text at (F, x, y); x, y in absolute screen coordinates*)
  590.     (* caret & selection *)
  591.     PROCEDURE RemoveSelection(F: Frame);
  592.     PROCEDURE SetSelection(F: Frame; beg, end: LONGINT);
  593.         (*forces range to visible bounds*)
  594.     PROCEDURE RemoveCaret(F: Frame);
  595.     PROCEDURE SetCaret(F: Frame; pos: LONGINT);
  596.         (*only done if within visible bounds*)
  597.     PROCEDURE Neutralize(F: Frame);
  598.         (*remove selection and caret*)
  599.     (* display range *)
  600.     PROCEDURE GetVisibleRange(F: Frame; VAR beg, end: LONGINT);
  601.         (*return visible range [beg, end) of F*)
  602.     PROCEDURE NotifyElems(F: Frame; VAR msg: NotifyMsg);
  603.         (*send notify message msg to all elements in visible range of F*)
  604.     PROCEDURE Show(F: Frame; pos: LONGINT);
  605.         (*removes global marks as needed and neutralizes F;
  606.         redisplays whole text if F.trailer = NIL*)
  607.     PROCEDURE Resize(F: Frame; x, y, w, h: INTEGER);
  608.         (*change frame size*)
  609.     (* contents update *)
  610.     PROCEDURE Update(F: Frame; VAR msg: UpdateMsg);
  611.         (*removes global marks as needed*)
  612.     (* user interface *)
  613.     PROCEDURE TrackLine(F: Frame; VAR x, y: INTEGER; VAR org: LONGINT; VAR keysum: SET);
  614.         (*track mouse to find line*)
  615.     PROCEDURE TrackWord(F: Frame; VAR x, y: INTEGER; VAR pos: LONGINT; VAR keysum: SET);
  616.         (*track mouse to find word*)
  617.     PROCEDURE TrackCaret(F: Frame; VAR x, y: INTEGER; VAR keysum: SET);
  618.         (*track mouse to place caret*)
  619.     PROCEDURE TrackSelection(F: Frame; VAR x, y: INTEGER; VAR keysum: SET);
  620.         (*track mouse to place selection; supports extension to beginning of line and extension
  621.         over subsequent viewers*)
  622.     PROCEDURE Call(F: Frame; pos: LONGINT; new: BOOLEAN);
  623.         (*call the command at (F.text, pos) with appropriate arguments;
  624.         force module reloading if new is set*)
  625.     PROCEDURE Write(F: Frame; ch: CHAR; fnt: Fonts.Font; col, voff: SHORTINT);
  626.         (*write character ch to F.text at current caret position;
  627.         if no other choice, use (fnt, col, voff) as attributes*)
  628.     PROCEDURE Edit(F: Frame; x, y: INTEGER; keysum: SET);
  629.         (*track mouse and support editing in frame F; calls Track*, Call, and Write*)
  630.     (* general *)
  631.     PROCEDURE Copy(F: Frame; VAR F1: Frame);
  632.         (*return a copy of F in F1*)
  633.     PROCEDURE Open(F: Frame; H: Display.Handler; T: WriteTexts.Text; pos: LONGINT;
  634.             left, right, top, bot, barW: INTEGER; opts: SET);
  635.         (*open frame F using handler H to display (T, pos) with border (left, right, top, bot),
  636.         scroll bar of width barW, and options opts*)
  637.     PROCEDURE Handle(f: Display.Frame; VAR msg: Display.FrameMsg);
  638.         (*dispatch message msg; calls NotifyElems, Show, Resize, Update, Write, Edit,
  639.         SetCaret, RemoveCaret, SetSelection, Neutralize, and Copy*)
  640.     PROCEDURE NotifyDisplay(T: Texts.Text; op: INTEGER; beg, end: LONGINT);
  641.         (*broadcasts an UpdateMsg for (T, op, beg, end) to all viewers*)
  642.     PROCEDURE Text(name: ARRAY OF CHAR; defParc: WriteTexts.Parc): WriteTexts.Text;
  643.         (*opens a standard Write text to be displayed in a Write frame*)
  644.     PROCEDURE NewText(T: WriteTexts.Text; pos: LONGINT): Frame;
  645.         (*opens a standard Write frame to be installed as content frame of a
  646.         MenuViewers viewer*)
  647. END WriteFrames.
  648. Module WriteParcs
  649. WriteParcs implements a proper extension of the parcs (paragraph controls) defined in module WriteTexts, adding interactive manipulation features for most parc attributes. Furthermore, the parc handler installed by WriteParcs supports querying and changing parc attributes using the message WriteParcs.StateMsg. This is used by the Write commands Write.Set and Write.Get. Hence, extended parcs defining new attributes can be defined that in turn extend the parameters taken by Write.Set and Write.Get. (The standard StyleElems take advantage of this by propagating changes to all parcs in a text that have the same name.)
  650. DEFINITION WriteParcs;
  651.     IMPORT
  652.         Display, Texts, WriteTexts, WriteFrames;
  653.     CONST
  654.         set = 0; get = 1;
  655.     TYPE
  656.         StateMsg = RECORD(Display.FrameMsg)
  657.             id: INTEGER;
  658.             par: Texts.Scanner;
  659.             log: Texts.Text;
  660.             unit: LONGINT;    (*units per device pixel*)
  661.             frame: WriteFrames.Frame
  662.         END;
  663.         defParc: WriteTexts.Parc;
  664.     PROCEDURE Prepare(P: WriteTexts.Parc; unit, indent: LONGINT; printing: BOOLEAN);
  665.         (*set width in order to occupy whole line*)
  666.     PROCEDURE Draw(P: WriteTexts.Parc; F: Display.Frame; unit: LONGINT;
  667.             col: SHORTINT; x0, y0: INTEGER);
  668.         (*draw parc*)
  669.     PROCEDURE Edit(P: WriteTexts.Parc; F: WriteFrames.Frame; unit: LONGINT;
  670.             x0, y0, x, y : INTEGER; keysum: SET);
  671.         (*edit parc*)
  672.     PROCEDURE SetAttr(P: WriteTexts.Parc; F: WriteFrames.Frame; unit: LONGINT;
  673.             VAR S: Texts.Scanner; log: Texts.Text);
  674.         (*scan attributes and values to be set using S and append error messages to log*)
  675.     PROCEDURE GetAttr(P: WriteTexts.Parc; F: WriteFrames.Frame; unit: LONGINT;
  676.             VAR S: Texts.Scanner; log: Texts.Text);
  677.         (*scan attributes to be queried using S and append result and error messages to log*)
  678.     PROCEDURE Handle(E: WriteTexts.Elem; VAR msg: Display.FrameMsg);
  679.         (*standard parc handler, calls WriteTexts.LoadParc, WriteTexts.StoreParc,
  680.         Prepare, Draw, Edit, SetAttr, and GetAttr*)
  681.     PROCEDURE Alloc;
  682.         (*allocate parc for loading*)
  683. END WriteParcs.
  684. Appendix C - Two Sample Element Implementations
  685. This appendix lists two sample element implementations that can be taken as a tutorial. The first, TestElems, implements nothing useful but shows a functional and commented element handler at a glance. Also, it can be used to test out the effect of having unloadable elements (called aliens) in a text. Finally, TestElems demonstrate the use of messages broadcasted to all visible elements. The second, ErrorElems, implements error marking elements that can be used to mark all errors found by a compiler within the source text. This second example shows how a handler should be decomposed and its components be exported in order to allow for further extension of the implemented element. Note that ErrorElems are temporary elements and thus never stored. Therefore, the handling of file I/O is missing in the ErrorElems implementation.
  686. TestElems
  687. MODULE TestElems;
  688.     IMPORT
  689.         Oberon, Input, Display, Viewers, Files, Fonts, Printer, Texts, WriteTexts, WriteFrames, WriteParcs;
  690.     CONST
  691.         mm = WriteTexts.mm;    rightKey = 0; middleKey = 1; leftKey = 2;
  692.     TYPE
  693.         TestElem = POINTER TO TestElemDesc;
  694.         TestElemDesc = RECORD(WriteTexts.ElemDesc)
  695.             data: ARRAY 8 OF CHAR    (*demonstrate use of element specific data*)
  696.         END;
  697.         NotifyMsg = RECORD(WriteFrames.NotifyMsg) END;
  698.     PROCEDURE WriteString(VAR r: Files.Rider; s: ARRAY OF CHAR);
  699.         VAR i: INTEGER;
  700.     BEGIN i := 0;
  701.         WHILE s[i] # 0X DO INC(i) END;
  702.         Files.WriteBytes(r, s, i + 1)
  703.     END WriteString;
  704.     PROCEDURE ReadString(VAR r: Files.Rider; VAR s: ARRAY OF CHAR);
  705.         VAR i: INTEGER; ch: CHAR;
  706.     BEGIN i := 0;
  707.         REPEAT Files.Read(r, ch); s[i] := ch; INC(i) UNTIL (ch = 0X) OR (i = LEN(s));
  708.         IF ch # 0X THEN s[0] := 0X END
  709.     END ReadString;
  710.     PROCEDURE* TestHandle(E: WriteTexts.Elem; VAR msg: Display.FrameMsg);
  711.         VAR e: TestElem; P: WriteTexts.Parc; x, y, w, h: INTEGER; keys, keysum: SET; visible: BOOLEAN;
  712.             fnt: Fonts.Font; col: SHORTINT; X0, Y0: INTEGER;
  713.     BEGIN
  714.         WITH E: TestElem DO
  715.             IF msg IS WriteFrames.PrepareMsg THEN    (*element is to be drawn or printed*)
  716.                 WITH msg: WriteFrames.PrepareMsg DO    (*adopt measures from environment*)
  717.                     P := WriteTexts.ParcBefore(WriteTexts.ElemBase(E), WriteTexts.ElemPos(E));
  718.                     E.H := P.lsp    (*; E.W := P.width - msg.indent    adapts to remaining space in line*)
  719.                 END
  720.             ELSIF msg IS WriteTexts.DrawMsg THEN    (*element is fully visible: draw it to the screen*)
  721.                 WITH msg: WriteTexts.DrawMsg DO
  722.                     Display.ReplConst(Display.white, msg.X0, msg.Y0,
  723.                         SHORT(E.W DIV msg.unit), SHORT(E.H DIV msg.unit), Display.replace)
  724.                 END
  725.             ELSIF msg IS WriteTexts.PrintMsg THEN    (*element is fully visible: print it*)
  726.                 WITH msg: WriteTexts.PrintMsg DO
  727.                     Printer.Line(msg.X0, msg.Y0, SHORT(E.W DIV msg.unit), SHORT(E.H DIV msg.unit))
  728.                 END
  729.             ELSIF msg IS NotifyMsg THEN    (*special viewer broadcast message*)
  730.                 WITH msg: NotifyMsg DO
  731.                     WriteFrames.LocateElem(msg.frame, E, visible, fnt, col, X0, Y0);
  732.                     IF visible THEN    (*if not clipped: update the single view in this frame*)
  733.                         Display.ReplConst(Display.white, X0 + 1, Y0 + 1,
  734.                             SHORT(E.W DIV msg.unit) - 2, SHORT(E.H DIV msg.unit) - 2, Display.invert)
  735.                     END
  736.                 END
  737.             ELSIF msg IS WriteTexts.LoadMsg THEN    (*load element specific data*)
  738.                 WITH msg: WriteTexts.LoadMsg DO
  739.                     ReadString(msg.r, E.data)
  740.                 END
  741.             ELSIF msg IS WriteTexts.StoreMsg THEN    (*store element specific data*)
  742.                 WITH msg: WriteTexts.StoreMsg DO
  743.                     WriteString(msg.r, "TestElems.Alloc");    (*write name of allocation procedure first*)
  744.                     WriteString(msg.r, E.data)
  745.                 END
  746.             ELSIF msg IS WriteTexts.CopyMsg THEN    (*copy element*)
  747.                 WITH msg: WriteTexts.CopyMsg DO
  748.                     IF msg.e = NIL THEN NEW(e); msg.e := e ELSE e := msg.e(TestElem) END;
  749.                     e.data := E.data    (*copy state into new element*)
  750.                 END
  751.             ELSIF msg IS WriteFrames.TrackMsg THEN    (*a mouse click hit the element*)
  752.                 WITH msg: WriteFrames.TrackMsg DO 
  753.                     IF msg.keys = {middleKey} THEN keysum := msg.keys;
  754.                         w := SHORT(E.W DIV msg.unit); h := SHORT(E.H DIV msg.unit);
  755.                         Oberon.RemoveMarks(msg.X0, msg.Y0, w, h);
  756.                         Display.ReplConst(15, msg.X0 + 1, msg.Y0 + 1, w - 2, h - 2, Display.invert);
  757.                         REPEAT Input.Mouse(keys, msg.X, msg.Y); keysum := keysum + keys;
  758.                             Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, msg.X, msg.Y)
  759.                         UNTIL keys = {};
  760.                         Display.ReplConst(15, msg.X0 + 1, msg.Y0 + 1, w - 2, h - 2, Display.invert);
  761.                         IF (keysum = {middleKey, leftKey}) & (E.W > 4 * mm) THEN
  762.                             DEC(E.W, 2 * mm); E.DX := E.W + 2 * mm; WriteTexts.ChangedElem(E)
  763.                         ELSIF msg.keys = {middleKey, rightKey} THEN
  764.                             INC(E.W, 2 * mm); E.DX := E.W + 2 * mm; WriteTexts.ChangedElem(E)
  765.                         END
  766.                     END
  767.                 END
  768.             END
  769.         END
  770.     END TestHandle;
  771.     PROCEDURE* MiscHandle(E: WriteTexts.Elem; VAR msg: Display.FrameMsg);
  772.         (*subclass handler of TestHandle*)
  773.     BEGIN
  774.         WITH E: TestElem DO
  775.             IF msg IS WriteTexts.StoreMsg THEN
  776.                 WITH msg: WriteTexts.StoreMsg DO
  777.                     (*write name of a nonexistent allocation procedure -> cannot be loaded again*)
  778.                     WriteString(msg.r, "TestElems.Unknown"); WriteString(msg.r, E.data)
  779.                 END
  780.             ELSE TestHandle(E, msg)    (*"super" call to inherited class*)
  781.             END
  782.         END
  783.     END MiscHandle;
  784.     PROCEDURE Alloc*;    (*allocation proc. for class TestElem; also installs handler*)
  785.         VAR e: TestElem;
  786.     BEGIN NEW(e); e.handle := TestHandle; Oberon.Par(WriteTexts.AllocPar).e := e
  787.     END Alloc;
  788.     (** commands **)
  789.     PROCEDURE InsertNew*;    (** W H demonstrates behaviour of trivial floating element**)
  790.         VAR S: Texts.Scanner; w: LONGINT;
  791.             e: TestElem; T: WriteTexts.Text; copyover: Oberon.CopyOverMsg;
  792.     BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); w := S.i; Texts.Scan(S);
  793.         NEW(e); WriteTexts.OpenElem(e, TestHandle, (w + 2)*mm, w*mm, S.i*mm); e.data := "testing";
  794.         T := WriteFrames.Text("", WriteParcs.defParc); WriteTexts.AppendElem(T, e);
  795.         copyover.text := T; copyover.beg := 0; copyover.end := T.len;
  796.         Oberon.FocusViewer.handle(Oberon.FocusViewer, copyover)
  797.     END InsertNew;
  798.     PROCEDURE InsertMisc*;
  799.         (** W H demonstrates handling of elements which cannot be loaded on opening**)
  800.         VAR S: Texts.Scanner; w: LONGINT;
  801.             e: TestElem; T: WriteTexts.Text; copyover: Oberon.CopyOverMsg;
  802.     BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); w := S.i; Texts.Scan(S);
  803.         NEW(e); WriteTexts.OpenElem(e, MiscHandle, (w + 2)*mm, w*mm, S.i*mm); e.data := "testing";
  804.         T := WriteFrames.Text("", WriteParcs.defParc); WriteTexts.AppendElem(T, e);
  805.         copyover.text := T; copyover.beg := 0; copyover.end := T.len;
  806.         Oberon.FocusViewer.handle(Oberon.FocusViewer, copyover)
  807.     END InsertMisc;
  808.     PROCEDURE Broadcast*;    (**demonstrate effect of special viewer broadcast message**)
  809.         VAR msg: NotifyMsg;
  810.     BEGIN Viewers.Broadcast(msg)
  811.     END Broadcast;
  812. END TestElems.
  813. ErrorElems
  814. DEFINITION ErrorElems;
  815.     IMPORT
  816.         Display, Fonts,
  817.         WriteTexts;
  818.     TYPE
  819.         Elem = POINTER TO ElemDesc;
  820.         ElemDesc = RECORD(WriteTexts.ElemDesc)
  821.             err: INTEGER;
  822.             msg: ARRAY 128 OF CHAR
  823.         END;
  824.         DeleteMsg = RECORD(Display.FrameMsg) END;
  825.         LocateMsg = RECORD(Display.FrameMsg)
  826.             pos: LONGINT
  827.         END;
  828.         font: Fonts.Font;
  829.     PROCEDURE ShowErrMsg(E: Elem; F: Display.Frame; col: SHORTINT; x0, y0, dw: INTEGER);
  830.     PROCEDURE Expand(E: Elem; unit: LONGINT);
  831.     PROCEDURE Reduce(E: Elem);
  832.     PROCEDURE Delete(E: Elem);
  833.     PROCEDURE Handle(E: WriteTexts.Elem; VAR msg: Display.FrameMsg);
  834.     PROCEDURE InsertAt(T: WriteTexts.Text; pos: LONGINT; err: INTEGER);
  835.     (* commands *)
  836.     PROCEDURE Unmark;
  837.     PROCEDURE Mark;
  838.     PROCEDURE LocateNext;
  839. END ErrorElems.
  840. MODULE ErrorElems;
  841.     IMPORT
  842.         Display, Input, Files, Fonts, Printer, Oberon, Texts, Viewers, MenuViewers,
  843.         WriteTexts, WriteFrames, WriteParcs;
  844.     CONST
  845.         ErrFile = "Oberon2Errors.Text"; ErrFont = "Syntax8.Scn.Fnt";
  846.         mm = WriteTexts.mm;    middleKey = 1; leftKey = 2;    CR = 0DX;
  847.     TYPE
  848.         Elem* = POINTER TO ElemDesc;
  849.         ElemDesc* = RECORD(WriteTexts.ElemDesc)
  850.             err*: INTEGER;
  851.             msg*: ARRAY 128 OF CHAR
  852.         END;
  853.         DeleteMsg* = RECORD(Display.FrameMsg) END;
  854.         LocateMsg* = RECORD(Display.FrameMsg)
  855.             pos*: LONGINT
  856.         END;
  857.         font*: Fonts.Font;
  858.         W: Texts.Writer;
  859.         lastTime: LONGINT;
  860.     PROCEDURE MarkedFrame(): WriteFrames.Frame;
  861.         VAR V: Viewers.Viewer;
  862.     BEGIN V := Oberon.MarkedViewer();
  863.         IF (V IS MenuViewers.Viewer) & (V.dsc.next IS WriteFrames.Frame) THEN
  864.             RETURN V.dsc.next(WriteFrames.Frame)
  865.         ELSE RETURN NIL
  866.         END
  867.     END MarkedFrame;
  868.     PROCEDURE Show(F: WriteFrames.Frame; pos: LONGINT);
  869.         VAR beg, end, delta: LONGINT;
  870.     BEGIN delta := 200;
  871.         LOOP WriteFrames.GetVisibleRange(F, beg, end);
  872.             IF (beg <= pos) & (pos < end) OR (beg = end) THEN EXIT END;
  873.             WriteFrames.Show(F, pos - delta); DEC(delta, 20)
  874.         END
  875.     END Show;
  876.     PROCEDURE Width(E: Elem): INTEGER;
  877.         VAR fnt: Fonts.Font; pat: Display.Pattern; i, px, dx, x, y, w, h: INTEGER;
  878.     BEGIN fnt := Fonts.This(ErrFont); i := 0; px := 0;
  879.         WHILE E.msg[i] # 0X DO
  880.             Display.GetChar(fnt.raster, E.msg[i], dx, x, y, w, h, pat); INC(px, dx); INC(i)
  881.         END;
  882.         RETURN px + 6
  883.     END Width;
  884.     PROCEDURE ShowErrMsg*(E: Elem; F: Display.Frame; col: SHORTINT; x0, y0, dw: INTEGER);
  885.         VAR fnt: Fonts.Font; pat: Display.Pattern; i, px, rm, dx, x, y, w, h: INTEGER; ch: CHAR;
  886.     BEGIN fnt := Fonts.This(ErrFont); i := 0; px := x0 + 3; rm := x0 + dw - 3; INC(y0, 2);
  887.         LOOP ch := E.msg[i]; INC(i);
  888.             IF ch = 0X THEN EXIT END;
  889.             Display.GetChar(fnt.raster, ch, dx, x, y, w, h, pat);
  890.             IF px + dx > rm THEN EXIT END;
  891.             Display.CopyPattern(col, pat, px + x, y0 + y, Display.invert); INC(px, dx)
  892.         END
  893.     END ShowErrMsg;
  894.     PROCEDURE Expand*(E: Elem; unit: LONGINT);
  895.         VAR S: Texts.Scanner; T: Texts.Text; n: INTEGER; ch: CHAR;
  896.     BEGIN NEW(T); Texts.Open(T, ErrFile); Texts.OpenScanner(S, T, 0);
  897.         REPEAT S.line := 0;
  898.             REPEAT Texts.Scan(S) UNTIL S.eot OR (S.line # 0)
  899.         UNTIL S.eot OR (S.class = Texts.Int) & (S.i = E.err);
  900.         IF ~S.eot THEN Texts.Read(S, ch); n := 0;
  901.             WHILE ~S.eot & (ch # CR) & (n + 1 < LEN(E.msg)) DO E.msg[n] := ch; INC(n); Texts.Read(S, ch) END;
  902.             E.msg[n] := 0X; E.W := Width(E) * unit; E.DX := E.W; WriteTexts.ChangedElem(E)
  903.         END
  904.     END Expand;
  905.     PROCEDURE Reduce*(E: Elem);
  906.     BEGIN E.W := 3 * mm; E.DX := E.W; E.msg[0] := 0X; WriteTexts.ChangedElem(E)
  907.     END Reduce;
  908.     PROCEDURE Delete*(E: Elem);
  909.         VAR T: WriteTexts.Text; pos: LONGINT;
  910.     BEGIN T := WriteTexts.ElemBase(E);
  911.         IF T # NIL THEN pos := WriteTexts.ElemPos(E); WriteTexts.Delete(T, pos, pos + 1) END
  912.     END Delete;
  913.     PROCEDURE Handle*(E: WriteTexts.Elem; VAR msg: Display.FrameMsg);
  914.         VAR e: Elem; pos: LONGINT; w, h: INTEGER; keys, keysum: SET;
  915.     BEGIN
  916.         WITH E: Elem DO w := SHORT(E.W DIV msg.unit); h := SHORT(E.H DIV msg.unit);
  917.             IF msg IS WriteTexts.DrawMsg THEN
  918.                 WITH msg: WriteTexts.DrawMsg DO
  919.                     Display.ReplConst(15, msg.X0 + 1, msg.Y0 + 2, w - 2, h, Display.replace);
  920.                     IF E.msg[0] # 0X THEN ShowErrMsg(E, msg.frame, msg.col, msg.X0, msg.Y0 + 2, w) END
  921.                 END
  922.             ELSIF msg IS WriteTexts.PrintMsg THEN
  923.                 WITH msg: WriteTexts.PrintMsg DO
  924.                     Printer.Line(msg.X0 + 1, msg.Y0 + 2, w - 2, h)
  925.                 END
  926.             ELSIF msg IS WriteTexts.CopyMsg THEN
  927.                 WITH msg: WriteTexts.CopyMsg DO
  928.                     IF msg.e = NIL THEN NEW(e); msg.e := e ELSE e := msg.e(Elem) END;
  929.                     e.err := E.err; e.msg := E.msg
  930.                 END
  931.             ELSIF msg IS WriteFrames.TrackMsg THEN
  932.                 WITH msg: WriteFrames.TrackMsg DO 
  933.                     IF msg.keys = {middleKey} THEN
  934.                         Oberon.RemoveMarks(msg.X0, msg.Y0, w, h);
  935.                         Display.ReplConst(15, msg.X0 + 2, msg.Y0 + 3, w - 4, h - 2, Display.invert);
  936.                         keysum := msg.keys;
  937.                         REPEAT Input.Mouse(keys, msg.X, msg.Y); keysum := keysum + keys;
  938.                             Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, msg.X, msg.Y);
  939.                         UNTIL keys = {};
  940.                         Display.ReplConst(15, msg.X0 + 2, msg.Y0 + 3, w - 4, h - 2, Display.invert);
  941.                         IF keysum = {middleKey} THEN
  942.                             IF E.msg[0] = 0X THEN Expand(E, msg.unit) ELSE Reduce(E) END
  943.                         ELSIF keysum = {middleKey, leftKey} THEN Delete(E)
  944.                         END
  945.                     END
  946.                 END
  947.             ELSIF msg IS DeleteMsg THEN Delete(E)
  948.             ELSIF msg IS LocateMsg THEN
  949.                 WITH msg: LocateMsg DO pos := WriteTexts.ElemPos(E);
  950.                     IF pos < msg.pos THEN msg.pos := pos END
  951.                 END
  952.             END
  953.         END
  954.     END Handle;
  955.     PROCEDURE InsertAt*(T: WriteTexts.Text; pos: LONGINT; err: INTEGER);
  956.         VAR e: Elem;
  957.     BEGIN NEW(e); WriteTexts.OpenElem(e, Handle, 3 * mm, 3 * mm, 3 * mm);
  958.         e.temp := TRUE; e.err := err; e.msg[0] := 0X; WriteTexts.InsertElem(T, pos, e)
  959.     END InsertAt;
  960.     (** commands **)
  961.     PROCEDURE Unmark*;
  962.         VAR F: WriteFrames.Frame; msg: DeleteMsg;
  963.     BEGIN F := MarkedFrame();
  964.         IF F # NIL THEN WriteTexts.Broadcast(F.text(WriteTexts.Text), 0, F.text.len, msg) END
  965.     END Unmark;
  966.     PROCEDURE Mark*;
  967.         VAR F: WriteFrames.Frame; S: Texts.Scanner; T: WriteTexts.Text;
  968.             text: Texts.Text; beg, end, time, pos, delta: LONGINT; err: INTEGER;
  969.     BEGIN Unmark; F := MarkedFrame(); Oberon.GetSelection(text, beg, end, time); delta := 0;
  970.         IF (F # NIL) & (time >= lastTime) THEN lastTime := time; T := F.text(WriteTexts.Text);
  971.             Texts.OpenScanner(S, text, beg);
  972.             LOOP S.line := 0;
  973.                 REPEAT Texts.Scan(S) UNTIL S.eot OR (S.line # 0) OR (S.class = Texts.Int);
  974.                 IF S.eot OR (S.line # 0) THEN EXIT END;
  975.                 pos := S.i;
  976.                 REPEAT Texts.Scan(S) UNTIL S.eot OR (S.line # 0) OR (S.class = Texts.Int);
  977.                 IF S.eot OR (S.line # 0) THEN EXIT END;
  978.                 err := SHORT(S.i); InsertAt(T, pos + delta, err); INC(delta);
  979.                 REPEAT Texts.Scan(S) UNTIL S.eot OR (S.line # 0)
  980.             END
  981.         END
  982.     END Mark;
  983.     PROCEDURE LocateNext*;
  984.         VAR F: WriteFrames.Frame; msg: LocateMsg; beg: LONGINT;
  985.     BEGIN F := MarkedFrame();
  986.         IF F # NIL THEN msg.pos := MAX(LONGINT);
  987.             IF F.hasCar THEN beg := F.carLoc.pos ELSE beg := 0 END;
  988.             WriteTexts.Broadcast(F.text(WriteTexts.Text), beg, F.text.len, msg);
  989.             IF msg.pos < MAX(LONGINT) THEN Show(F, msg.pos); WriteFrames.SetCaret(F, msg.pos + 1) END
  990.         END
  991.     END LocateNext;
  992. BEGIN font := Fonts.This(ErrFont); Texts.OpenWriter(W); lastTime := -1
  993. END ErrorElems.
  994. WriteParcs.Alloc
  995. WriteParcs.Alloc
  996. LineElems.Alloc
  997. LineElems.Alloc
  998. WriteParcs.Alloc
  999. LineElems.Alloc
  1000. WriteParcs.Alloc
  1001. WriteParcs.Alloc
  1002. WriteParcs.Alloc
  1003. WriteParcs.Alloc
  1004. TableElems.Alloc
  1005. Syntax10.Scn.Fnt
  1006. Syntax12.Scn.Fnt
  1007. Syntax12i.Scn.Fnt
  1008. /nohead "h"/noline "hvlrbt"/col "RLN"/left 10/table
  1009. Table of Contents        3
  1010. Introduction        4
  1011. The Write Core System    1.1  Approaching Extensibility - Coping with Complexity    5
  1012.  2  Load & Store - The Write File Format    7
  1013.  3  Visual Interpretation of Write Texts - WriteFrames    8
  1014.  4  From Elements to Parcs - Introduction of Paragraph Structure    10
  1015.  5  Printing - Options and Compromises    12
  1016. Elements: Write Extensions    2.1 The Structure of an Element Implementation    13
  1017.  2  Temporary Elements - Error Elements    14
  1018.  3  Static Elements - Chart Elements    15
  1019.  4  Adaptive Elements - Line Elements    15
  1020.  5  Active Elements - Clock Elements, Icon Elements    16
  1021.  6  Interactive Elements - Popup Elements, Style Elements    17
  1022.  7  Wrapper Elements - Graphics Elements, Picture Elements    18
  1023.  8  Structuring Elements - Fold Elements    19
  1024.  9  Formatting Elements - Table Elements    21
  1025.      10  Special Purpose Elements: Building Applications using Write    23
  1026. Summary and Conclusions        25
  1027. References        26
  1028. Appendix    
  1029. A.  Write User Manual    27
  1030. B.  Interfaces of the Write Core Modules    33
  1031. C.  Two Sample Element Implementations    42
  1032. WriteParcs.Alloc
  1033. WriteParcs.Alloc
  1034. StyleElems.Alloc
  1035. main part
  1036. StyleElems.Alloc
  1037. main part
  1038. StyleElems.Alloc
  1039. heading
  1040. StyleElems.Alloc
  1041. begin figure
  1042. TableElems.Alloc
  1043. Syntax10.Scn.Fnt
  1044. /top 8/nohead "v"/noline "hvlr"/col "LL"/table
  1045. attribute    typical values
  1046. font family    Times Roman, Syntax, Helvetica, ...
  1047. font size    height in points
  1048. font style    bold, italic, ...
  1049. color    foreground, background, ...
  1050. vertical offset    character displacement relative to the base line
  1051. WriteParcs.Alloc
  1052. WriteParcs.Alloc
  1053. StyleElems.Alloc
  1054. StyleElems.Alloc
  1055. begin table
  1056. StyleElems.Alloc
  1057. WriteParcs.Alloc
  1058. StyleElems.Alloc
  1059. StyleElems.Alloc
  1060. begin figure
  1061. GraphicElems.Alloc
  1062. Rectangles
  1063. Syntax10.Scn.Fnt
  1064. Elektra.Scn.Fnt
  1065. StyleElems.Alloc
  1066. StyleElems.Alloc
  1067. heading
  1068. StyleElems.Alloc
  1069. begin table
  1070. StyleElems.Alloc
  1071. StyleElems.Alloc
  1072. begin table
  1073. StyleElems.Alloc
  1074. heading
  1075. StyleElems.Alloc
  1076. begin figure
  1077. GraphicElems.Alloc
  1078. Curves
  1079. Syntax10.Scn.Fnt
  1080. resulting base line offset
  1081. Syntax10i.Scn.Fnt
  1082. base line
  1083. Math10.Scn.Fnt
  1084. Elektra.Scn.Fnt
  1085. Elektra.Scn.Fnt
  1086. Elektra.Scn.Fnt
  1087. hangover above
  1088. hangover below
  1089. Elektra.Scn.Fnt
  1090. Elektra.Scn.Fnt
  1091. Rectangles
  1092. resulting
  1093. line height
  1094. GraphicElems.Alloc
  1095. Syntax10i.Scn.Fnt
  1096. base line
  1097. Syntax10.Scn.Fnt
  1098. = n*lsp   n > 0
  1099. resulting base line offset = dsr + k*lsp
  1100. line height
  1101. resulting
  1102. Elektra.Scn.Fnt
  1103. Elektra.Scn.Fnt
  1104. Math10.Scn.Fnt
  1105. Elektra.Scn.Fnt
  1106. hangover below
  1107. hangover above
  1108. Rectangles
  1109. StyleElems.Alloc
  1110. StyleElems.Alloc
  1111. begin table
  1112. WriteParcs.Alloc
  1113. StyleElems.Alloc
  1114. StyleElems.Alloc
  1115. heading
  1116. StyleElems.Alloc
  1117. begin figure
  1118. GraphicElems.Alloc
  1119. Rectangles
  1120. Elektra.Scn.Fnt
  1121. Syntax10.Scn.Fnt
  1122. insert parc (block adjusting)
  1123. text (left adjusted)
  1124. Elektra.Scn.Fnt
  1125. Elektra.Scn.Fnt
  1126. width
  1127. before inserting a new parc
  1128. after inserting the new parc
  1129. . . .
  1130. . . .
  1131. left adjusted
  1132. range
  1133. Elektra.Scn.Fnt
  1134. StyleElems.Alloc
  1135. StyleElems.Alloc
  1136. begin table
  1137. StyleElems.Alloc
  1138. StyleElems.Alloc
  1139. begin table
  1140. StyleElems.Alloc
  1141. heading
  1142. StyleElems.Alloc
  1143. begin table
  1144. StyleElems.Alloc
  1145. main part
  1146. StyleElems.Alloc
  1147. heading
  1148. StyleElems.Alloc
  1149. begin figure
  1150. TableElems.Alloc
  1151. Syntax10.Scn.Fnt
  1152. Syntax12.Scn.Fnt
  1153. /top 8/nohead "v"/noline "hvlr"/col "LLL"/table
  1154. defining module    message    semantics
  1155. WriteTexts    Copy    return copy of receiver
  1156.     Draw    draw self to frame
  1157.     Load    load self from file
  1158.     Print    print self
  1159.     Store    store self to file
  1160. WriteFrames    Notify    used for broadcasts to visible elements
  1161.     Prepare    prepare self for drawing/printing
  1162.     Track    handle mouse tracking and mouse clicks
  1163. WriteParcs    State    get/set parc attributes
  1164. WriteParcs.Alloc
  1165. WriteParcs.Alloc
  1166. StyleElems.Alloc
  1167. heading
  1168. StyleElems.Alloc
  1169. begin table
  1170. GraphicElems.Alloc
  1171. Rectangles
  1172. GraphicElems.Alloc
  1173. Syntax8.Scn.Fnt
  1174. undeclared identifier
  1175. Rectangles
  1176. StyleElems.Alloc
  1177. begin commands
  1178. LineElems.Alloc
  1179. StyleElems.Alloc
  1180. heading
  1181. StyleElems.Alloc
  1182. begin figure
  1183. TableElems.Alloc
  1184. Syntax10.Scn.Fnt
  1185. Syntax10i.Scn.Fnt
  1186. /top 8/nohead "v"/noline "hvlr"/col "CC"/row "LCCCCCCCC"/table
  1187. input    ChartElems.Create width 400
  1188. WriteParcs.Alloc
  1189. WriteParcs.Alloc
  1190. ChartElems.Alloc
  1191. ChartElems.Alloc
  1192. ChartElems.Alloc
  1193. ChartElems.Alloc
  1194. ChartElems.Alloc
  1195. ChartElems.Alloc
  1196. ChartElems.Alloc
  1197. ChartElems.Alloc
  1198. TableElems.Alloc
  1199. Syntax10.Scn.Fnt
  1200. Syntax10i.Scn.Fnt
  1201. /top 8/nohead "v"/noline "hvlr"/col "CC"/row "LC"/table
  1202. input    ChartElems.Create height 200
  1203. \16\74\39\47\95\23\75\37    
  1204. WriteParcs.Alloc
  1205. WriteParcs.Alloc
  1206. ChartElems.Alloc
  1207. ChartElems.Alloc
  1208. ChartElems.Alloc
  1209. ChartElems.Alloc
  1210. ChartElems.Alloc
  1211. ChartElems.Alloc
  1212. ChartElems.Alloc
  1213. ChartElems.Alloc
  1214. StyleElems.Alloc
  1215. begin commands
  1216. LineElems.Alloc
  1217. StyleElems.Alloc
  1218. heading
  1219. WriteParcs.Alloc
  1220. LineElems.Alloc
  1221. LineElems.Alloc
  1222. LineElems.Alloc
  1223. LineElems.Alloc
  1224. LineElems.Alloc
  1225. LineElems.Alloc
  1226. LineElems.Alloc
  1227. LineElems.Alloc
  1228. StyleElems.Alloc
  1229. begin figure
  1230. StyleElems.Alloc
  1231. begin commands
  1232. LineElems.Alloc
  1233. StyleElems.Alloc
  1234. heading
  1235. StyleElems.Alloc
  1236. begin figure
  1237. ClockElems.Alloc
  1238. IconElems.Alloc
  1239. StyleElems.Alloc
  1240. WriteParcs.Alloc
  1241. StyleElems.Alloc
  1242. StyleElems.Alloc
  1243. begin commands
  1244. LineElems.Alloc
  1245. StyleElems.Alloc
  1246. heading
  1247. StyleElems.Alloc
  1248. begin figure
  1249. GraphicElems.Alloc
  1250. Syntax12m.Scn.Fnt
  1251. Write...
  1252. Rectangles
  1253. Syntax10.Scn.Fnt
  1254. Write.Recall @
  1255. Write.InsertParc @
  1256. Write.Locate ^
  1257. Write.SelectParc @
  1258. Write.Set line 10
  1259. Write.Set line 34
  1260. Write.Set line 51
  1261. Write.Set lead 10
  1262. Write.Set lead 34
  1263. Write.Set lead 68
  1264. Curves
  1265. StyleElems.Alloc
  1266. begin figure
  1267. GraphicElems.Alloc
  1268. Rectangles
  1269. Syntax8.Scn.Fnt
  1270. Chapter Heading
  1271. StyleElems.Alloc
  1272. begin commands
  1273. LineElems.Alloc
  1274. StyleElems.Alloc
  1275. heading
  1276. StyleElems.Alloc
  1277. begin figure
  1278. PictElems.Alloc
  1279. Grapes.Pict
  1280. StyleElems.Alloc
  1281. begin commands
  1282. LineElems.Alloc
  1283. StyleElems.Alloc
  1284. heading
  1285. StyleElems.Alloc
  1286. begin figure
  1287. GraphicElems.Alloc
  1288. Curves
  1289. Syntax10i.Scn.Fnt
  1290. opened fold
  1291. closed fold
  1292. Syntax10.Scn.Fnt
  1293. END GetName;
  1294. IF s.class = Texts.Name THEN COPY(s.s, n) ELSE n := "" END
  1295. Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
  1296. BEGIN
  1297. VAR s: Texts.Scanner;
  1298. PROCEDURE GetName(VAR n: Name);
  1299. Elektra.Scn.Fnt
  1300. Elektra.Scn.Fnt
  1301. PROCEDURE GetName(VAR n: Name);
  1302. StyleElems.Alloc
  1303. begin commands
  1304. LineElems.Alloc
  1305. StyleElems.Alloc
  1306. heading
  1307. StyleElems.Alloc
  1308. begin table
  1309. StyleElems.Alloc
  1310. StyleElems.Alloc
  1311. begin figure
  1312. TableElems.Alloc
  1313. Syntax10.Scn.Fnt
  1314. Syntax10i.Scn.Fnt
  1315. /top 8/nohead "v"/noline "hvlr"/col "-lcrn"/table
  1316. cell type \ col. format    left    center    right    numeric
  1317. alpha    abc    abc    abc    abc
  1318. integer    42    42    42    42
  1319. real    1.3    1.23    1.3    1.3
  1320. negative    -12.34    -12.34    -12.34    -12.34
  1321. WriteParcs.Alloc
  1322. WriteParcs.Alloc
  1323. StyleElems.Alloc
  1324. begin figure
  1325. TableElems.Alloc
  1326. Syntax10.Scn.Fnt
  1327. Syntax10i.Scn.Fnt
  1328. /top 8/bot 8/row "-tclb"/table
  1329. row format \ cell type    alpha    subtable 1    subtable 2    subtable 3    subtable 4
  1330. top    Yepp    
  1331. center    Yepp    
  1332. line    Yepp    
  1333. bottom    Yepp    
  1334. WriteParcs.Alloc
  1335. WriteParcs.Alloc
  1336. TableElems.Alloc
  1337. Syntax10.Scn.Fnt
  1338. Syntax8.Scn.Fnt
  1339. /table
  1340. single line
  1341. WriteParcs.Alloc
  1342. WriteParcs.Alloc
  1343. TableElems.Alloc
  1344. Syntax10.Scn.Fnt
  1345. /nohead "*"/table
  1346. double
  1347. WriteParcs.Alloc
  1348. WriteParcs.Alloc
  1349. TableElems.Alloc
  1350. Syntax10.Scn.Fnt
  1351. /nohead "*"/table
  1352. WriteParcs.Alloc
  1353. WriteParcs.Alloc
  1354. TableElems.Alloc
  1355. Syntax10.Scn.Fnt
  1356. /nohead "*"/table
  1357. double
  1358. w/ offset
  1359. WriteParcs.Alloc
  1360. WriteParcs.Alloc
  1361. TableElems.Alloc
  1362. Syntax10.Scn.Fnt
  1363. Syntax8.Scn.Fnt
  1364. /table
  1365. single line
  1366. WriteParcs.Alloc
  1367. WriteParcs.Alloc
  1368. TableElems.Alloc
  1369. Syntax10.Scn.Fnt
  1370. /nohead "*"/table
  1371. double
  1372. WriteParcs.Alloc
  1373. WriteParcs.Alloc
  1374. TableElems.Alloc
  1375. Syntax10.Scn.Fnt
  1376. /nohead "*"/table
  1377. WriteParcs.Alloc
  1378. WriteParcs.Alloc
  1379. TableElems.Alloc
  1380. Syntax10.Scn.Fnt
  1381. /nohead "*"/table
  1382. double
  1383. w/ offset
  1384. WriteParcs.Alloc
  1385. WriteParcs.Alloc
  1386. TableElems.Alloc
  1387. Syntax10.Scn.Fnt
  1388. Syntax8.Scn.Fnt
  1389. /table
  1390. single line
  1391. WriteParcs.Alloc
  1392. WriteParcs.Alloc
  1393. TableElems.Alloc
  1394. Syntax10.Scn.Fnt
  1395. /nohead "*"/table
  1396. double
  1397. WriteParcs.Alloc
  1398. WriteParcs.Alloc
  1399. TableElems.Alloc
  1400. Syntax10.Scn.Fnt
  1401. /nohead "*"/table
  1402. WriteParcs.Alloc
  1403. WriteParcs.Alloc
  1404. TableElems.Alloc
  1405. Syntax10.Scn.Fnt
  1406. /nohead "*"/table
  1407. double
  1408. w/ offset
  1409. WriteParcs.Alloc
  1410. WriteParcs.Alloc
  1411. TableElems.Alloc
  1412. Syntax10.Scn.Fnt
  1413. Syntax8.Scn.Fnt
  1414. /table
  1415. single line
  1416. WriteParcs.Alloc
  1417. WriteParcs.Alloc
  1418. TableElems.Alloc
  1419. Syntax10.Scn.Fnt
  1420. /nohead "*"/table
  1421. double
  1422. WriteParcs.Alloc
  1423. WriteParcs.Alloc
  1424. TableElems.Alloc
  1425. Syntax10.Scn.Fnt
  1426. /nohead "*"/table
  1427. WriteParcs.Alloc
  1428. WriteParcs.Alloc
  1429. TableElems.Alloc
  1430. Syntax10.Scn.Fnt
  1431. /nohead "*"/table
  1432. double
  1433. w/ offset
  1434. WriteParcs.Alloc
  1435. WriteParcs.Alloc
  1436. StyleElems.Alloc
  1437. begin figure
  1438. GraphicElems.Alloc
  1439. Curves
  1440. Syntax10.Scn.Fnt
  1441. cell boundaries
  1442. cell contents
  1443. Elektra.Scn.Fnt
  1444. Elektra.Scn.Fnt
  1445. Elektra.Scn.Fnt
  1446. Elektra.Scn.Fnt
  1447. Elektra.Scn.Fnt
  1448. //////
  1449. //////
  1450. bottom
  1451. right
  1452. top frame
  1453. bottom frame
  1454. frame
  1455. right
  1456. frame
  1457. vertical
  1458. separation
  1459. vertical
  1460. header
  1461. separation
  1462. hor.header
  1463. horizontal
  1464. StyleElems.Alloc
  1465. begin figure
  1466. TableElems.Alloc
  1467. Syntax10.Scn.Fnt
  1468. /top 8/noline "h"/table
  1469. a    b    c
  1470. d    e    f
  1471. WriteParcs.Alloc
  1472. TableElems.Alloc
  1473. Syntax10.Scn.Fnt
  1474. /top 8/noline "v"/table
  1475. WriteParcs.Alloc
  1476. TableElems.Alloc
  1477. Syntax10.Scn.Fnt
  1478. /top 8/noline "thb"/table
  1479. a    b    c
  1480. d    e    f
  1481. g    h    i
  1482. WriteParcs.Alloc
  1483. TableElems.Alloc
  1484. Syntax10.Scn.Fnt
  1485. /top 8/noline "lvr"/nohead "*"/table
  1486. a    b    c
  1487. d    e    f
  1488. g    h    i
  1489. WriteParcs.Alloc
  1490. TableElems.Alloc
  1491. Syntax10.Scn.Fnt
  1492. /top 8/noline "hvlrtb"/table
  1493. a    b    c
  1494. d    e    f
  1495. g    h    i
  1496. WriteParcs.Alloc
  1497. StyleElems.Alloc
  1498. begin figure
  1499. TableElems.Alloc
  1500. Syntax10.Scn.Fnt
  1501. /bot 0/top 0/wid 1/grid 0/nohead "*"/noline "*"/table
  1502. WriteParcs.Alloc
  1503. WriteParcs.Alloc
  1504. TableElems.Alloc
  1505. Syntax10.Scn.Fnt
  1506. /top 8/table
  1507. x    yyyyyyyyyyyyyyy    z
  1508. WriteParcs.Alloc
  1509. TableElems.Alloc
  1510. Syntax10.Scn.Fnt
  1511. /top 8/nohead "*"/table
  1512. x    yyyyyyyyyyyyyyy    z
  1513. WriteParcs.Alloc
  1514. TableElems.Alloc
  1515. Syntax10.Scn.Fnt
  1516. /top 8/nohead "h"/noline "r"/table
  1517. a    bbbbbb
  1518. WriteParcs.Alloc
  1519. TableElems.Alloc
  1520. Syntax10.Scn.Fnt
  1521. /top 8/nohead "*"/noline "l"/table
  1522. aaaaa    b
  1523. WriteParcs.Alloc
  1524. TableElems.Alloc
  1525. Syntax10.Scn.Fnt
  1526. /top 8/nohead "*"/noline "r"/table
  1527. a    bbbbbb
  1528. WriteParcs.Alloc
  1529. TableElems.Alloc
  1530. Syntax10.Scn.Fnt
  1531. /top 8/nohead "*"/noline "l"/table
  1532. aaaaa    b
  1533. WriteParcs.Alloc
  1534. TableElems.Alloc
  1535. Syntax10.Scn.Fnt
  1536. /top 8/nohead "h"/noline "r"/table
  1537. WriteParcs.Alloc
  1538. TableElems.Alloc
  1539. Syntax10.Scn.Fnt
  1540. /top 8/nohead "*"/noline "l"/table
  1541. WriteParcs.Alloc
  1542. TableElems.Alloc
  1543. Syntax10.Scn.Fnt
  1544. /top 8/nohead "*"/noline "lr"/table
  1545. WriteParcs.Alloc
  1546. TableElems.Alloc
  1547. Syntax10.Scn.Fnt
  1548. /top 8/nohead "*"/noline "l"/table
  1549. WriteParcs.Alloc
  1550. TableElems.Alloc
  1551. Syntax10.Scn.Fnt
  1552. /top 8/nohead "*"/noline "r"/table
  1553. WriteParcs.Alloc
  1554. TableElems.Alloc
  1555. Syntax10.Scn.Fnt
  1556. /top 8/nohead "*"/noline "l"/table
  1557. WriteParcs.Alloc
  1558. TableElems.Alloc
  1559. Syntax10.Scn.Fnt
  1560. /top 8/nohead "*"/noline "lr"/table
  1561. WriteParcs.Alloc
  1562. TableElems.Alloc
  1563. Syntax10.Scn.Fnt
  1564. /top 8/nohead "*"/noline "l"/table
  1565. WriteParcs.Alloc
  1566. TableElems.Alloc
  1567. Syntax10.Scn.Fnt
  1568. /top 8/nohead "h"/noline "r"/table
  1569. WriteParcs.Alloc
  1570. TableElems.Alloc
  1571. Syntax10.Scn.Fnt
  1572. /top 8/nohead "*"/noline "l"/table
  1573. WriteParcs.Alloc
  1574. TableElems.Alloc
  1575. Syntax10.Scn.Fnt
  1576. /top 8/nohead "*"/noline "lr"/table
  1577. WriteParcs.Alloc
  1578. TableElems.Alloc
  1579. Syntax10.Scn.Fnt
  1580. /top 8/nohead "*"/noline "l"/table
  1581. WriteParcs.Alloc
  1582. TableElems.Alloc
  1583. Syntax10.Scn.Fnt
  1584. /top 8/nohead "*"/noline "r"/table
  1585. WriteParcs.Alloc
  1586. TableElems.Alloc
  1587. Syntax10.Scn.Fnt
  1588. /top 8/nohead "*"/noline "l"/table
  1589. WriteParcs.Alloc
  1590. TableElems.Alloc
  1591. Syntax10.Scn.Fnt
  1592. /top 8/nohead "*"/noline "lr"/table
  1593. WriteParcs.Alloc
  1594. TableElems.Alloc
  1595. Syntax10.Scn.Fnt
  1596. /top 8/nohead "*"/noline "l"/table
  1597. WriteParcs.Alloc
  1598. StyleElems.Alloc
  1599. begin commands
  1600. LineElems.Alloc
  1601. StyleElems.Alloc
  1602. begin figure
  1603. TableElems.Alloc
  1604. Syntax10.Scn.Fnt
  1605. /top 8/nohead "v"/noline "vlr"/col "LLL"/row "LCCCCC"/table
  1606. option    effect (measures in 1/10 mm)    argument values
  1607. /columns  string    
  1608. /rows  string    
  1609. /noheads  string    
  1610. /nolines  string    
  1611. WriteParcs.Alloc
  1612. WriteParcs.Alloc
  1613. TableElems.Alloc
  1614. Syntax10.Scn.Fnt
  1615. /left 0/right 0/nohead "*"/noline "*"/col "LL"/table
  1616. control column formatting
  1617. string = {L|C|R|N|-}.
  1618. WriteParcs.Alloc
  1619. WriteParcs.Alloc
  1620. TableElems.Alloc
  1621. Syntax10.Scn.Fnt
  1622. /left 0/right 0/nohead "*"/noline "*"/col "LL"/table
  1623. L    left flush
  1624. C    centered
  1625. R    right flush
  1626. N    numeric (period adjusted)
  1627. -    default
  1628. WriteParcs.Alloc
  1629. WriteParcs.Alloc
  1630. TableElems.Alloc
  1631. Syntax10.Scn.Fnt
  1632. /left 0/right 0/nohead "*"/noline "*"/col "LL"/table
  1633. control row formatting
  1634. string = {B|L|C|T|-}.
  1635. WriteParcs.Alloc
  1636. WriteParcs.Alloc
  1637. TableElems.Alloc
  1638. Syntax10.Scn.Fnt
  1639. /left 0/right 0/nohead "*"/noline "*"/col "LL"/table
  1640. B    bottom flush
  1641. L    common baseline
  1642. C    centered
  1643. T    top flush
  1644. -    default
  1645. WriteParcs.Alloc
  1646. WriteParcs.Alloc
  1647. TableElems.Alloc
  1648. Syntax10.Scn.Fnt
  1649. /left 0/right 0/nohead "*"/noline "*"/col "LL"/table
  1650. turn header lines off
  1651. string = {H|V |*}.
  1652. WriteParcs.Alloc
  1653. WriteParcs.Alloc
  1654. TableElems.Alloc
  1655. Syntax10.Scn.Fnt
  1656. /left 0/right 0/nohead "*"/noline "*"/col "LL"/table
  1657. H    horizontal
  1658. V    vertical
  1659. *    all
  1660. WriteParcs.Alloc
  1661. WriteParcs.Alloc
  1662. TableElems.Alloc
  1663. Syntax10.Scn.Fnt
  1664. /left 0/right 0/nohead "*"/noline "*"/col "LL"/table
  1665. turn separation lines off
  1666. string = {L|R|B|T|H|V|*}.
  1667. WriteParcs.Alloc
  1668. WriteParcs.Alloc
  1669. TableElems.Alloc
  1670. Syntax10.Scn.Fnt
  1671. /left 0/right 0/nohead "*"/noline "*"/col "LL"/table
  1672. L    left frame
  1673. R    right frame
  1674. B    bottom frame
  1675. T    top frame
  1676. H    horizontal separation
  1677. V    vertical separation
  1678. *    all
  1679. WriteParcs.Alloc
  1680. WriteParcs.Alloc
  1681. TableElems.Alloc
  1682. Syntax10.Scn.Fnt
  1683. /left 0/right 0/nohead "*"/noline "*"/table
  1684. /period  string
  1685. /left  integer
  1686. /right  integer
  1687. /bottom  integer
  1688. /top  integer
  1689. /grid  integer
  1690. WriteParcs.Alloc
  1691. WriteParcs.Alloc
  1692. TableElems.Alloc
  1693. Syntax10.Scn.Fnt
  1694. /left 0/right 0/nohead "*"/noline "*"/table
  1695. first char redefines period (e.g. ",")
  1696. left cell margin
  1697. right cell margin
  1698. bottom cell margin
  1699. top cell margin
  1700. cell width grid *)
  1701. WriteParcs.Alloc
  1702. WriteParcs.Alloc
  1703. TableElems.Alloc
  1704. Syntax10.Scn.Fnt
  1705. /noheads "*"/nolines "*"/col "RL"/table
  1706. *)    A parc at the beginning of the table defining text introduces a minimal                          
  1707.     cell height as well as a cell height grid (if the grid option of the parc is set)
  1708. WriteParcs.Alloc
  1709. StyleElems.Alloc
  1710. heading
  1711. WriteParcs.Alloc
  1712. DebSim.Alloc
  1713. DebSim.Alloc
  1714. DebSim.Alloc
  1715. DebSim.Alloc
  1716. StyleElems.Alloc
  1717. begin figure
  1718. StyleElems.Alloc
  1719. main part
  1720. StyleElems.Alloc
  1721. StyleElems.Alloc
  1722. StyleElems.Alloc
  1723. begin figure
  1724. TableElems.Alloc
  1725. Syntax10.Scn.Fnt
  1726. Syntax12.Scn.Fnt
  1727. Syntax12i.Scn.Fnt
  1728. /left 20/right 20/nohead "*"/noline "hlrbt"/col "RL"/table
  1729. [Ad85]    Adobe Systems, Inc. PostScript Language Reference Manual.
  1730.     Addison-Wesley, Reading, MA, 1985.
  1731. [Fr90]    M. Franz. The Implementation of MacOberon.
  1732.     Tech. Report 141, Institut f
  1733. r Computersysteme, ETH Zurich, October 1990.
  1734. [Gu90]    J. Gutknecht. The Oberon Guide. System Release 1.2.
  1735.     Tech. Report 138, Institut f
  1736. r Computersysteme, ETH Zurich, October 1990.
  1737. [Hi91]    U. Hiestand. Leda - Das Dokumentenverarbeitungssystem f
  1738. r Oberon.
  1739.     Tech. Report to appear, Institut f
  1740. r Computersysteme, ETH Zurich.
  1741. [Szy90a]    C.A. Szyperski.  Towards Object-Oriented Structures for Open Operating Systems.
  1742.     Presented at the Workshop on Object Orientation in Operating Systems
  1743.     at OOPSLA-ECOOP '90, Ottawa, Oct 1990.
  1744. [Szy90b]    C.A. Szyperski. The Carrier / Rider Separation. A New Structuring Concept for
  1745.     Open Operating Systems. Submitted to ECOOP '91, Geneva, July 1991.
  1746. [Te90]    J. Templ. SPARC Oberon - User's Guide and Implementation.
  1747.     Tech. Report 133, Institut f
  1748. r Computersysteme, ETH Zurich, June 1990.
  1749. [WeGaMa89]    A. Weinand, E. Gamma, R. Marty. Design and Implementation of ET++,
  1750.     a Seamless Object-Oriented Application Framework.
  1751.     Structured Programming, 10:2, 63-87, February 1989.
  1752. [Wi83]    G. Williams. The Lisa Computer System. BYTE 2, February 1983.
  1753. [Wi88a]    N. Wirth. Type Extensions.
  1754.     ACM Trans. on Progr. Lang. and Systems. 10:2, 204-214. April 1988.
  1755. [Wi88b]    N. Wirth. The Programming Language Oberon.
  1756.     Software - Practice and Experience. 18:7, 671-690. July 1988.
  1757. [Wi89]    N. Wirth. Modula-2 and Object-Oriented Programming.
  1758.     First International Conf. on Modula-2, Bled, Yugoslavia, Oct 1989.
  1759.     (also: Tech. Report 117, Institut f
  1760. r Computersysteme, ETH Zurich, October 1989.)
  1761. [WiGu88]    N. Wirth, J. Gutknecht. The Oberon System.
  1762.     Tech. Report 88, Institut f
  1763. r Computersysteme, ETH Zurich, July 1988.
  1764. WriteParcs.Alloc
  1765. WriteParcs.Alloc
  1766. StyleElems.Alloc
  1767. main part
  1768. StyleElems.Alloc
  1769. sub-heading
  1770. StyleElems.Alloc
  1771. sub-heading
  1772. StyleElems.Alloc
  1773. begin figure
  1774. GraphicElems.Alloc
  1775. Syntax10.Scn.Fnt
  1776. tabulator settings
  1777. Elektra.Scn.Fnt
  1778. Elektra.Scn.Fnt
  1779. separation line
  1780. Rectangles
  1781. StyleElems.Alloc
  1782. begin figure
  1783. TableElems.Alloc
  1784. Syntax10.Scn.Fnt
  1785. Syntax10i.Scn.Fnt
  1786. /top 8/nohead "v"/noline "hvlr"/col "LLL"/table
  1787. where    buttons    effect
  1788. above separation line, left end    middle    set left margin
  1789. above separation line, left end    middle + right    symmetrically set left & right margin
  1790. above separation line, right end    middle    set right margin
  1791. above separation line, right end    middle + right    symmetrically set left & right margin
  1792. above separation line, towards left end    middle    left flush formatting
  1793. above separation line, in the middle    middle    centered formatting
  1794. above separation line, towards right end    middle    right flush formatting
  1795. above separation line    middle + left    block formatting
  1796. below separation line, no tab mark hit    middle    create new tab
  1797. below separation line, tab mark hit    middle    move tab
  1798. below separation line, tab mark hit    middle + right    move tab and all subsequent tabs in synch
  1799. below separation line, tab mark hit    middle + left    delete tab
  1800. WriteParcs.Alloc
  1801. WriteParcs.Alloc
  1802. StyleElems.Alloc
  1803. sub-heading
  1804. StyleElems.Alloc
  1805. sub-heading
  1806. StyleElems.Alloc
  1807. begin commands
  1808. StyleElems.Alloc
  1809. sub-heading
  1810. StyleElems.Alloc
  1811. begin commands
  1812. LineElems.Alloc
  1813. StyleElems.Alloc
  1814. sub-heading
  1815. StyleElems.Alloc
  1816. begin figure
  1817. TableElems.Alloc
  1818. Syntax10.Scn.Fnt
  1819. Syntax12.Scn.Fnt
  1820. Syntax10i.Scn.Fnt
  1821. /top 8/nohead "v"/noline "hvlr"/col "LL"/table
  1822. option    function
  1823. "/a"    alternating pages - page number on right side for even pages
  1824. "/c" number    request a certain number of copies to be printed (1..9)
  1825. "/f" name    select font (other than the system default) for headers and page numbers
  1826. "/h"    print a header line (file name plus print date) on each page
  1827. "/l" number    shift print image on page (correction, in 1/10 mm)
  1828. "/p" number    start page numbering at a certain number (otherwise it starts at 0)
  1829. "/p" "f"    suppress page number and header on first page (as set by /p)
  1830. "/p" "n"    suppress page numbers
  1831. "/s" number [number]    select page(s) to print (numbers corresponds to the ones set by /p)
  1832. WriteParcs.Alloc
  1833. WriteParcs.Alloc
  1834. StyleElems.Alloc
  1835. sub-heading
  1836. StyleElems.Alloc
  1837. begin figure
  1838. TableElems.Alloc
  1839. Syntax10.Scn.Fnt
  1840. Syntax10i.Scn.Fnt
  1841. /top 8/nohead "v"/nolines "hvlr"/col "LLNL"/table
  1842. attribute    value(s)    default    function
  1843. "adjust"    block | center | left | right    #left    formatting mode
  1844. "break"    before | normal    #normal    if before: force page break before parc
  1845. "grid"    on | off    #off    line grid
  1846. "lead"    name | number | "default"    0    leading paragraph space
  1847. "left"    number | "default"    0    left margin
  1848. "line"    name | number | "default"    34    minimal line height
  1849. "tabs"    ("*" number | {number} "~")    #~    tab spacing (every n, or enumerated)
  1850. "width"    number | "default"    1650    maximal line width
  1851. WriteParcs.Alloc
  1852. WriteParcs.Alloc
  1853. StyleElems.Alloc
  1854. sub-heading
  1855. StyleElems.Alloc
  1856. sub-heading
  1857. StyleElems.Alloc
  1858. begin commands
  1859. LineElems.Alloc
  1860. StyleElems.Alloc
  1861. main part
  1862. StyleElems.Alloc
  1863. begin figure
  1864. TableElems.Alloc
  1865. Syntax10.Scn.Fnt
  1866. /top 8/nohead "v"/noline "hvlr"/col "LNNL"/table
  1867.  module    lines of source    object code [bytes]     functionality
  1868.  WriteTexts    450    5 500     elements, load/store
  1869.  WriteFrames    950    14 800     selfcontained frame
  1870.  WriteParcs    400    6 700     interactive parcs
  1871.  Write    750    10'900     commands & printing
  1872.  Texts    n/a    6 900     attributed texts
  1873.  TextFrames    n/a    10 000     selfcontained frame
  1874.  Edit    n/a    4 800     commands & printing
  1875.  LineElems    100    1 600    various lines
  1876.  GraphicElems    200    2 300    encapsulated graphics
  1877.  PopupElems    200    3 500    popup menu buttons
  1878.  TableElems    500    8 500    table formatting
  1879. WriteParcs.Alloc
  1880. WriteParcs.Alloc
  1881. StyleElems.Alloc
  1882. sub-heading
  1883. StyleElems.Alloc
  1884. begin figure
  1885. TableElems.Alloc
  1886. Syntax10.Scn.Fnt
  1887. Syntax12.Scn.Fnt
  1888. /top 8/nohead "*"/noline "hvlr"/col "C"/table
  1889. WriteParcs.Alloc
  1890. WriteParcs.Alloc
  1891. TableElems.Alloc
  1892. Syntax10.Scn.Fnt
  1893. /nohead "*"/noline "*"/col "LCR"/table
  1894.  1 mm = 36'000 units                  1 point = 12'700 units                1 inch = 914'400 units 
  1895. WriteParcs.Alloc
  1896. WriteParcs.Alloc
  1897. TableElems.Alloc
  1898. Syntax10.Scn.Fnt
  1899. /top 8/nohead "v"/noline "hvlrb"/col "NNL"/table
  1900. dots per inch    units per dot     examples
  1901. 72    12'700
  1902. 91    10'000     Ceres-1/2 monochrome monitors; Ceres *.Scn.Fnt files
  1903. 144    6'350
  1904. 200    4'572
  1905. 240    3'810
  1906. 300    3'048     Ceres laser printer; Ceres *.Pr3.Fnt and *.Lm3.Fnt files
  1907. 400    2'286
  1908. 600    1'524
  1909. 900    1'016
  1910. 1200    762
  1911. WriteParcs.Alloc
  1912. WriteParcs.Alloc
  1913. StyleElems.Alloc
  1914. StyleElems.Alloc
  1915. begin table
  1916. WriteParcs.Alloc
  1917. WriteParcs.Alloc
  1918. StyleElems.Alloc
  1919. sub-heading
  1920. StyleElems.Alloc
  1921. begin table
  1922. WriteParcs.Alloc
  1923. StyleElems.Alloc
  1924. sub-heading
  1925. StyleElems.Alloc
  1926. begin table
  1927. StyleElems.Alloc
  1928. main part
  1929. StyleElems.Alloc
  1930. sub-heading
  1931. StyleElems.Alloc
  1932. begin listing
  1933. LineElems.Alloc
  1934. LineElems.Alloc
  1935. StyleElems.Alloc
  1936. sub-heading
  1937. StyleElems.Alloc
  1938. begin listing
  1939. LineElems.Alloc
  1940. StyleElems.Alloc
  1941. begin listing
  1942. LineElems.Alloc
  1943. LineElems.Alloc
  1944.